From d24a22fe9a414bf37859967848c45f2a7494193d Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 15 May 2011 16:06:18 +0400 Subject: s4-python: add an option for just fixing gpo folders --- source4/scripting/bin/upgradeprovision | 385 +++++++++++++++++---------------- 1 file changed, 196 insertions(+), 189 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 8c79917d5e..4e48a48b45 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -151,6 +151,8 @@ 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("--fixntacl", action="store_true", + help="Only fix NT ACLs in sysvol / netlogon share") parser.add_option("--full", action="store_true", help="Perform full upgrade of the samdb (schema, configuration, new objects, ...") @@ -1609,80 +1611,81 @@ 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) - 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") - 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, 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() - deltaattr = None -# 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 - 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") + # 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, 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() + deltaattr = None + # 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 + 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 @@ -1691,112 +1694,112 @@ if __name__ == '__main__': 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) + 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: - 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) + # 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) > 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) - - # 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)): + 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) + + # 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: + 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 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") + "You should restart upgradeprovision with --full") except IOError, e: message(ERROR, "Setting ACL not supported on your filesystem") else: @@ -1804,25 +1807,29 @@ if __name__ == '__main__': 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 ! - # 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, "Reopenning 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) + "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, "Reopenning 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) + else: + ldbs.groupedRollback() + message(SIMPLE, "ACLs fixed !") except StandardError, err: message(ERROR, "A problem occurred while trying to upgrade your " - "provision. A full backup is located at %s" % backupdir) + "provision. A full backup is located at %s" % backupdir) if opts.debugall or opts.debugchange: (typ, val, tb) = sys.exc_info() traceback.print_exception(typ, val, tb) -- cgit From 6071ed67bf413b0f9245be9038e2e600d8ebb5aa Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Fri, 20 May 2011 19:43:19 +0400 Subject: s4-python: move function find_provision_key_parameters to provision namespace as it can be used not only for upgradeprovision --- source4/scripting/bin/upgradeprovision | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 4e48a48b45..e58a2647b6 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -44,7 +44,7 @@ from ldb import (SCOPE_SUBTREE, SCOPE_BASE, FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE, MessageElement, Message, Dn) from samba import param, dsdb, Ldb -from samba.provision import (get_domain_descriptor, +from samba.provision import (get_domain_descriptor, find_provision_key_parameters, get_config_descriptor, ProvisioningError, get_last_provision_usn, get_max_usn, update_provision_usn, setup_path) @@ -52,7 +52,7 @@ from samba.schema import get_linked_attributes, Schema, get_schema_descriptor 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, + get_ldbs, usn_in_range, identic_rename, get_diff_sddls, update_secrets, CHANGE, ERROR, SIMPLE, CHANGEALL, GUESS, CHANGESD, PROVISION, -- cgit From a0db60d3eabc309c3e65915b00e023acd9a13897 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 26 Apr 2011 00:04:32 +0400 Subject: Add a script for renaming a DC --- source4/scripting/bin/renamedc | 200 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100755 source4/scripting/bin/renamedc (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/renamedc b/source4/scripting/bin/renamedc new file mode 100755 index 0000000000..0915b15783 --- /dev/null +++ b/source4/scripting/bin/renamedc @@ -0,0 +1,200 @@ +#!/usr/bin/env python +# vim: expandtab +# +# Copyright (C) Matthieu Patou 2011 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import optparse +import sys +# Allow to run from s4 source directory (without installing samba) +sys.path.insert(0, "bin/python") + +import ldb +import samba +import samba.getopt as options +import os + +from samba.credentials import DONT_USE_KERBEROS +from samba.auth import system_session +from samba import param +from samba.provision import find_provision_key_parameters, secretsdb_self_join +from samba.upgradehelpers import get_ldbs, get_paths + + +__docformat__ = "restructuredText" + +parser = optparse.OptionParser("provision [options]") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +parser.add_option("--oldname", + help="Old DC name") +parser.add_option("--newname", + help="New DC name") + +opts = parser.parse_args()[0] + +if len(sys.argv) == 1: + opts.interactive = True +lp = sambaopts.get_loadparm() +smbconf = lp.configfile + +creds = credopts.get_credentials(lp) +creds.set_kerberos_state(DONT_USE_KERBEROS) + + +if __name__ == '__main__': + global defSDmodified + defSDmodified = False + # 1) First get files paths + paths = get_paths(param, smbconf=smbconf) + # Get ldbs with the system session, it is needed for searching + # provision parameters + session = system_session() + + ldbs = get_ldbs(paths, creds, session, lp) + ldbs.sam.transaction_start() + ldbs.secrets.transaction_start() + + if opts.oldname is None or opts.newname is None: + raise Exception("Option oldname or newname is missing") + res = ldbs.sam.search(expression="(&(name=%s)(serverReferenceBL=*))" % opts.oldname) + if res is None or len(res) != 1: + raise Exception("Wrong number of result returned, are you sure of the old name %s" % + opts.oldname) + + # Ok got it then check that the new name is not used as well + res2 = ldbs.sam.search(expression="(&(name=%s)(objectclass=computer))" % opts.newname) + if len(res2) != 0: + raise Exception("Seems that %s is a name that already exists, pick another one" % + opts.newname) + + names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, + paths, smbconf, lp) + + #First rename the entry + # provision put the name in upper case so let's do it too ! + newdn = str(res[0].dn).replace("CN=%s" % opts.oldname, "CN=%s" % opts.newname.upper()) + dnobj = ldb.Dn(ldbs.sam, newdn) + ldbs.sam.rename(res[0].dn, dnobj) + + # Then change password and samaccountname and dnshostname + msg = ldb.Message(dnobj) + machinepass = samba.generate_random_password(128, 255) + mputf16 = machinepass.encode('utf-16-le') + + account = "%s$" % opts.newname.upper() + msg["clearTextPassword"] = ldb.MessageElement(mputf16, + ldb.FLAG_MOD_REPLACE, + "clearTextPassword") + + msg["sAMAccountName"] = ldb.MessageElement(account, + ldb.FLAG_MOD_REPLACE, + "sAMAccountName") + + msg["dNSHostName"] = ldb.MessageElement("%s.%s" % (opts.newname, + names.dnsdomain), + ldb.FLAG_MOD_REPLACE, + "dNSHostName") + ldbs.sam.modify(msg) + + # Do a self join one more time to resync the secrets file + res = ldbs.sam.search(expression=("dn=%s" % newdn), + attrs=["msDs-keyVersionNumber", "serverReferenceBL"]) + assert(len(res) == 1) + kvno = int(str(res[0]["msDs-keyVersionNumber"])) + serverbldn = ldb.Dn(ldbs.sam, str(res[0]["serverReferenceBL"])) + + secrets_msg = ldbs.secrets.search(expression="sAMAccountName=%s$" % + opts.oldname.upper(), + attrs=["secureChannelType"]) + + secChanType = int(secrets_msg[0]["secureChannelType"][0]) + + secretsdb_self_join(ldbs.secrets, domain=names.domain, + realm=names.realm, + domainsid=names.domainsid, + dnsdomain=names.dnsdomain, + netbiosname=opts.newname.upper(), + machinepass=machinepass, + key_version_number=kvno, + secure_channel_type=secChanType) + + # Update RID set reference as there is no back link for the moment. + + res = ldbs.sam.search(expression="(objectClass=rIDSet)", base=newdn, attrs=[]) + assert(len(res) == 1) + newridset = str(res[0].dn) + msg = ldb.Message(dnobj) + + msg["rIDSetReferences"] = ldb.MessageElement(newridset, + ldb.FLAG_MOD_REPLACE, + "rIDSetReferences") + ldbs.sam.modify(msg) + + # Update the server's sites configuration + if False: + # Desactivated for the moment we have a couple of issues with site + # renaming first one is that it's currently forbidden + # second one is that a lot of links are not backlinked + # and so won't be updated when the DN change (ie. fmsowner ...) + serverbl = str(serverbldn) + dnparts = serverbl.split(",") + dnparts[0] = "CN=%s" % opts.newname.upper() + newserverref = ",".join(dnparts) + + newserverrefdn = ldb.Dn(ldbs.sam, newserverref) + + ldbs.sam.rename(serverbldn, newserverrefdn) + + msg = ldb.Message(newserverrefdn) + msg["dNSHostName"] = ldb.MessageElement("%s.%s" % (opts.newname, + names.dnsdomain), + ldb.FLAG_MOD_REPLACE, + "dNSHostName") + ldbs.sam.modify(msg) + + try: + ldbs.sam.transaction_prepare_commit() + ldbs.secrets.transaction_prepare_commit() + except Exception: + ldbs.sam.rollback() + ldbs.secrets.rollback() + sys.exit(1) + + try: + ldbs.sam.transaction_commit() + ldbs.secrets.transaction_commit() + except Exception: + ldbs.sam.rollback() + ldbs.secrets.rollback() + + # All good so far + #print lp.get("private dir") + cf = open(lp.configfile) + ncfname = "%s.new" % lp.configfile + newconf = open(ncfname, 'w') + for l in cf.readlines(): + if l.find("netbios name") > 0: + newconf.write("\tnetbios name = %s\n" % opts.newname.upper()) + else: + newconf.write(l) + newconf.close() + cf.close() + os.rename(ncfname, lp.configfile) + -- cgit From 7d59e9c549933c33182fbb0a7227be0eb69b4892 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 2 Jun 2011 17:09:17 +1000 Subject: s4-ipv6: added IPv6 support to samba_dnsupdate --- source4/scripting/bin/samba_dnsupdate | 38 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index e86fba2983..cefe3ce859 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -89,6 +89,15 @@ if len(IPs) == 0: print "No IP interfaces - skipping DNS updates" sys.exit(0) +IP6s = [] +IP4s = [] +for i in IPs: + if i.find(':') != -1: + IP6s.append(i) + else: + IP4s.append(i) + + if opts.verbose: print "IPs: %s" % IPs @@ -122,7 +131,7 @@ class dnsobj(object): if self.type == 'SRV': self.dest = list[2].lower() self.port = list[3] - elif self.type == 'A': + elif self.type in ['A', 'AAAA']: self.ip = list[2] # usually $IP, which gets replaced elif self.type == 'CNAME': self.dest = list[2].lower() @@ -132,6 +141,7 @@ class dnsobj(object): def __str__(self): if d.type == "A": return "%s %s %s" % (self.type, self.name, self.ip) + if d.type == "AAAA": return "%s %s %s" % (self.type, self.name, self.ip) if d.type == "SRV": return "%s %s %s %s" % (self.type, self.name, self.dest, self.port) if d.type == "CNAME": return "%s %s %s" % (self.type, self.name, self.dest) @@ -178,7 +188,7 @@ def check_dns_name(d): if opts.verbose: print "Failed to find DNS entry %s" % d return False - if d.type == 'A': + if d.type in ['A', 'AAAA']: # we need to be sure that our IP is there for rdata in ans: if str(rdata) == str(d.ip): @@ -247,6 +257,8 @@ def call_nsupdate(d): f = os.fdopen(tmp_fd, 'w') if d.type == "A": f.write("update add %s %u A %s\n" % (normalised_name, default_ttl, d.ip)) + if d.type == "AAAA": + f.write("update add %s %u AAAA %s\n" % (normalised_name, default_ttl, d.ip)) if d.type == "SRV": if d.existing_port is not None: f.write("update delete %s SRV 0 %s %s %s\n" % (normalised_name, d.existing_weight, @@ -382,16 +394,28 @@ for line in file: if line == '' or line[0] == "#": continue d = parse_dns_line(line, sub_vars) + if d.type == 'A' and len(IP4s) == 0: + continue + if d.type == 'AAAA' and len(IP6s) == 0: + continue dns_list.append(d) # now expand the entries, if any are A record with ip set to $IP # then replace with multiple entries, one for each interface IP for d in dns_list: - if d.type == 'A' and d.ip == "$IP": - d.ip = IPs[0] - for i in range(len(IPs)-1): + if d.ip != "$IP": + continue + if d.type == 'A': + d.ip = IP4s[0] + for i in range(len(IP4s)-1): + d2 = dnsobj(str(d)) + d2.ip = IP4s[i+1] + dns_list.append(d2) + if d.type == 'AAAA': + d.ip = IP6s[0] + for i in range(len(IP6s)-1): d2 = dnsobj(str(d)) - d2.ip = IPs[i+1] + d2.ip = IP6s[i+1] dns_list.append(d2) # now check if the entries already exist on the DNS server @@ -412,7 +436,7 @@ for d in update_list: if am_rodc: if d.name.lower() == domain.lower(): continue - if d.type != 'A': + if not d.type in [ 'A', 'AAAA' ]: call_rodc_update(d) else: call_nsupdate(d) -- cgit From c4e43f9dff108f762eb1f7271ddb896b9b22e478 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 3 Jun 2011 11:43:09 +1000 Subject: s4-ipv6: don't add link local addresses to DNS these make no sense as DNS addresses --- source4/scripting/bin/samba_dnsupdate | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index cefe3ce859..c82e06bb6f 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -93,7 +93,9 @@ IP6s = [] IP4s = [] for i in IPs: if i.find(':') != -1: - IP6s.append(i) + if i.find('%') == -1: + # we don't want link local addresses for DNS updates + IP6s.append(i) else: IP4s.append(i) -- cgit From 0e1d7eb429a3c6b34ed659f86b8d7a92434acd37 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 4 Jun 2011 08:20:54 +1000 Subject: s4-testparm: testparm is now part of samba_tool --- source4/scripting/bin/setup_dns.sh | 2 +- source4/scripting/bin/testparm | 219 ------------------------------------- 2 files changed, 1 insertion(+), 220 deletions(-) delete mode 100755 source4/scripting/bin/testparm (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/setup_dns.sh b/source4/scripting/bin/setup_dns.sh index de4485fc07..646ee81f26 100755 --- a/source4/scripting/bin/setup_dns.sh +++ b/source4/scripting/bin/setup_dns.sh @@ -13,7 +13,7 @@ IP="$3" RSUFFIX=$(echo $DOMAIN | sed s/[\.]/,DC=/g) [ -z "$PRIVATEDIR" ] && { - PRIVATEDIR=$(bin/testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null) + PRIVATEDIR=$(bin/samba_tool testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null) } OBJECTGUID=$(bin/ldbsearch -s base -H "$PRIVATEDIR/sam.ldb" -b "CN=NTDS Settings,CN=$HOSTNAME,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=$RSUFFIX" objectguid|grep ^objectGUID| cut -d: -f2) diff --git a/source4/scripting/bin/testparm b/source4/scripting/bin/testparm deleted file mode 100755 index c30e46148c..0000000000 --- a/source4/scripting/bin/testparm +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python -# vim: expandtab ft=python -# -# Unix SMB/CIFS implementation. -# Test validity of smb.conf -# Copyright (C) Karl Auer 1993, 1994-1998 -# -# Extensively modified by Andrew Tridgell, 1995 -# Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002 -# Updated for Samba4 by Andrew Bartlett 2006 -# Converted to Python by Jelmer Vernooij 2010 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Testbed for loadparm.c/params.c -# -# This module simply loads a specified configuration file and -# if successful, dumps it's contents to stdout. Note that the -# operation is performed with DEBUGLEVEL at 3. -# -# Useful for a quick 'syntax check' of a configuration file. -# - -import logging -import optparse -import os -import sys - -# Find right directory when running from source tree -sys.path.insert(0, "bin/python") - -import samba -from samba import getopt as options - -# Here we do a set of 'hard coded' checks for bad -# configuration settings. - -def do_global_checks(lp, logger): - valid = True - - netbios_name = lp.get("netbios name") - if not samba.valid_netbios_name(netbios_name): - logger.error("netbios name %s is not a valid netbios name", - netbios_name) - valid = False - - workgroup = lp.get("workgroup") - if not samba.valid_netbios_name(workgroup): - logger.error("workgroup name %s is not a valid netbios name", - workgroup) - valid = False - - lockdir = lp.get("lockdir") - - if not os.path.isdir(lockdir): - logger.error("lock directory %s does not exist", lockdir) - valid = False - - piddir = lp.get("pid directory") - - if not os.path.isdir(piddir): - logger.error("pid directory %s does not exist", piddir) - valid = False - - winbind_separator = lp.get("winbind separator") - - if len(winbind_separator) != 1: - logger.error("the 'winbind separator' parameter must be a single " - "character.") - valid = False - - if winbind_separator == '+': - logger.error("'winbind separator = +' might cause problems with group " - "membership.") - valid = False - - return valid - - -def allow_access(deny_list, allow_list, cname, caddr): - raise NotImplementedError(allow_access) - - -def do_share_checks(lp, logger): - valid = True - for s in lp.services(): - if len(s) > 12: - logger.warning("You have some share names that are longer than 12 " - "characters. These may not be accessible to some older " - "clients. (Eg. Windows9x, WindowsMe, and not listed in " - "smbclient in Samba 3.0.)") - break - - for s in lp.services(): - deny_list = lp.get("hosts deny", s) - allow_list = lp.get("hosts allow", s) - if deny_list: - for entry in deny_list: - if "*" in entry or "?" in entry: - logger.error("Invalid character (* or ?) in hosts deny " - "list (%s) for service %s.", entry, s) - valid = False - - if allow_list: - for entry in allow_list: - if "*" in entry or "?" in entry: - logger.error("Invalid character (* or ?) in hosts allow " - "list (%s) for service %s.", entry, s) - valid = False - return valid - -def check_client_access(lp, cname, caddr): - # this is totally ugly, a real `quick' hack - for s in lp.services(): - if (allow_access(lp.get("hosts deny"), lp.get("hosts allow"), cname, - caddr) and - allow_access(lp.get("hosts deny", s), lp.get("hosts allow", s), - cname, caddr)): - logger.info("Allow connection from %s (%s) to %s", cname, caddr, s) - else: - logger.info("Deny connection from %s (%s) to %s", cname, caddr, s) - - -if __name__ == '__main__': - parser = optparse.OptionParser("testparm [OPTION...] [host-name] [host-ip]") - parser.add_option("--section-name", type="string", metavar="SECTION", - help="Limit testparm to a named section") - parser.add_option("--parameter-name", type="string", metavar="PARAMETER", - help="Limit testparm to a named parameter") - parser.add_option("--client-name", type="string", metavar="HOSTNAME", - help="Client DNS name for 'hosts allow' checking " - "(should match reverse lookup)") - parser.add_option("--client-ip", type="string", metavar="IP", - help="Client IP address for 'hosts allow' checking") - parser.add_option("--suppress-prompt", action="store_true", default=False, - help="Suppress prompt for enter") - parser.add_option("-v", "--verbose", action="store_true", - default=False, help="Show default options too") - parser.add_option_group(options.VersionOptions(parser)) - # We need support for smb.conf macros before this will work again - parser.add_option("--server", type="string", - help="Set %%L macro to servername") - # These are harder to do with the new code structure - parser.add_option("--show-all-parameters", action="store_true", - default=False, help="Show the parameters, type, possible values") - - sambaopts = options.SambaOptions(parser) - parser.add_option_group(sambaopts) - - opts, args = parser.parse_args() - -# -# if (show_all_parameters) { -# show_parameter_list() -# exit(0) -# } - - if len(args) > 0: - cname = args[0] - else: - cname = None - if len(args) > 1: - caddr = args[1] - else: - caddr = None - - if cname is not None and caddr is None: - print "Both a DNS name and an IP address are required for the host " \ - "access check." - sys.exit(1) - -# FIXME: We need support for smb.conf macros before this will work again -# -# if (new_local_machine) { -# set_local_machine_name(new_local_machine, True) -# } - - lp = sambaopts.get_loadparm() - - # We need this to force the output - samba.set_debug_level(2) - - logger = logging.getLogger("testparm") - logger.addHandler(logging.StreamHandler(sys.stdout)) - - logger.info("Loaded smb config files from %s", lp.configfile) - logger.info("Loaded services file OK.") - - valid = do_global_checks(lp, logger) - valid = valid and do_share_checks(lp, logger) - if cname is not None: - check_client_access(lp, cname, caddr) - else: - if opts.section_name is not None or opts.parameter_name is not None: - if opts.parameter_name is None: - lp[opts.section_name].dump(sys.stdout, lp.default_service, - opts.verbose) - else: - print lp.get(opts.parameter_name, opts.section_name) - else: - if not opts.suppress_prompt: - print "Press enter to see a dump of your service definitions\n" - sys.stdin.readline() - lp.dump(sys.stdout, opts.verbose) - if valid: - sys.exit(0) - else: - sys.exit(1) -- cgit From 473b3446998ca49e6597e30f8bbca43fd7215769 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 4 Jun 2011 08:22:30 +1000 Subject: s4-script: install some of the samba scripts in bin/ during build --- source4/scripting/bin/wscript_build | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 source4/scripting/bin/wscript_build (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/wscript_build b/source4/scripting/bin/wscript_build new file mode 100644 index 0000000000..e52b32bc02 --- /dev/null +++ b/source4/scripting/bin/wscript_build @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +bld.SAMBA_SCRIPT('samba_dnsupdate', pattern='samba_dnsupdate', installdir='.') +bld.SAMBA_SCRIPT('samba_spnupdate', pattern='samba_spnupdate', installdir='.') +bld.SAMBA_SCRIPT('upgradeprovision', pattern='upgradeprovision', installdir='.') -- cgit From c091a92be51e8c14bf0b51ab83319fbcb704c91f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 15:43:40 +1000 Subject: s4-param Remove 'sam database' parameter This now just relies on the private dir parameter, which remains. Andrew Bartlett --- source4/scripting/bin/samba_dnsupdate | 2 +- source4/scripting/bin/samba_spnupdate | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index c82e06bb6f..0a13dd7c9b 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -222,7 +222,7 @@ def get_subst_vars(): global lp, am_rodc vars = {} - samdb = SamDB(url=lp.get("sam database"), session_info=system_session(), + samdb = SamDB(url=lp.samdb_url(), session_info=system_session(), lp=lp) vars['DNSDOMAIN'] = lp.get('realm').lower() diff --git a/source4/scripting/bin/samba_spnupdate b/source4/scripting/bin/samba_spnupdate index 1794f2bd26..fe3fcfc675 100755 --- a/source4/scripting/bin/samba_spnupdate +++ b/source4/scripting/bin/samba_spnupdate @@ -103,9 +103,9 @@ try: else: credentials = None - samdb = SamDB(url=lp.get("sam database"), session_info=system_session(), credentials=credentials, lp=lp) + samdb = SamDB(url=lp.samdb_url(), session_info=system_session(), credentials=credentials, lp=lp) except ldb.LdbError, (num, msg): - print("Unable to open sam database %s : %s" % (lp.get("sam database"), msg)) + print("Unable to open sam database %s : %s" % (lp.samdb_url(), msg)) sys.exit(1) -- cgit From 1565da76947f91add10a54096cdfe2ab67917b32 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 15:47:44 +1000 Subject: s4-param Remove 'secrets database' parameter This is now just secrets.ldb in the private dir, which remains. --- source4/scripting/bin/samba_spnupdate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/samba_spnupdate b/source4/scripting/bin/samba_spnupdate index fe3fcfc675..9f8f4073d3 100755 --- a/source4/scripting/bin/samba_spnupdate +++ b/source4/scripting/bin/samba_spnupdate @@ -82,7 +82,7 @@ def get_subst_vars(samdb): try: private_dir = lp.get("private dir") - secrets_path = os.path.join(private_dir, lp.get("secrets database")) + secrets_path = os.path.join(private_dir, "secrets.ldb") secrets_db = Ldb(url=secrets_path, session_info=system_session(), credentials=creds, lp=lp) -- cgit From a58e69a734085f9963b60042be3d9a33a90616a7 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 7 Jun 2011 13:46:24 +1000 Subject: s4-dns: fixed samba_tool -> samba-tool --- source4/scripting/bin/setup_dns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/setup_dns.sh b/source4/scripting/bin/setup_dns.sh index 646ee81f26..bc2ae96b84 100755 --- a/source4/scripting/bin/setup_dns.sh +++ b/source4/scripting/bin/setup_dns.sh @@ -13,7 +13,7 @@ IP="$3" RSUFFIX=$(echo $DOMAIN | sed s/[\.]/,DC=/g) [ -z "$PRIVATEDIR" ] && { - PRIVATEDIR=$(bin/samba_tool testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null) + PRIVATEDIR=$(bin/samba-tool testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null) } OBJECTGUID=$(bin/ldbsearch -s base -H "$PRIVATEDIR/sam.ldb" -b "CN=NTDS Settings,CN=$HOSTNAME,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=$RSUFFIX" objectguid|grep ^objectGUID| cut -d: -f2) -- cgit From b14bdf431b49a674bceab7f8f81ae98d938b92f6 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 5 Jun 2011 17:26:07 +0400 Subject: s4-upgradeprovision: Fix an error, so that cursddl and refsddl are not the same Thanks to Dirk Paulli for pointing it with his bug report. --- source4/scripting/bin/upgradeprovision | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index e58a2647b6..9d3b683940 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -905,7 +905,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): cursddl = cursd.as_sddl(names.domainsid) refsd = ndr_unpack(security.descriptor, str(reference[0]["nTSecurityDescriptor"])) - refsddl = cursd.as_sddl(names.domainsid) + refsddl = refsd.as_sddl(names.domainsid) if get_diff_sddls(refsddl, cursddl) == "": message(CHANGE, "sd are identical") -- cgit From 0065742909453f85709635aa44787b6998cccfc3 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 17:13:26 +0400 Subject: s4-upgradeprovision: handle_special_attributes don't really need ranges of USNs, just the information if we are using replPropertyMetadata for attribute selection --- source4/scripting/bin/upgradeprovision | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 9d3b683940..c625625c44 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -297,7 +297,7 @@ def print_provision_key_parameters(names): message(GUESS, "domainlevel :" + str(names.domainlevel)) -def handle_special_case(att, delta, new, old, usn, basedn, aldb): +def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb): """Define more complicate update rules for some attributes :param att: The attribute to be updated @@ -305,7 +305,8 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb): between the updated object and the reference one :param new: The reference object :param old: The Updated object - :param usn: The highest usn modified by a previous (upgrade)provision + :param useReplMetadata: A boolean that indicate if the update process + use replPropertyMetaData to decide what has to be updated. :param basedn: The base DN of the provision :param aldb: An ldb object used to build DN :return: True to indicate that the attribute should be kept, False for @@ -315,7 +316,7 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb): # We do most of the special case handle if we do not have the # highest usn as otherwise the replPropertyMetaData will guide us more # correctly - if usn is None: + if not useReplMetadata: if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT," "CN=Services,CN=Configuration,%s" % basedn) @@ -889,7 +890,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): # idea to change it delta.remove(att) continue - if handle_special_case(att, delta, reference, current, usns, basedn, samdb): + if handle_special_case(att, delta, reference, current, True, basedn, samdb): # This attribute is "complicated" to handle and handling # was done in handle_special_case continue @@ -959,7 +960,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): if not hashOverwrittenAtt.has_key(att): if msgElt.flags() != FLAG_MOD_ADD: if not handle_special_case(att, delta, reference, current, - usns, basedn, samdb): + False, basedn, samdb): if opts.debugchange or opts.debugall: try: dump_denied_change(dn, att, -- cgit From d9abcc93847fedf3ca272fe69cde0a92e76c85d0 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 17:50:00 +0400 Subject: s4-upgradeprovision: split update_present in two functions depending on the method used In order to make the function a bit more clearer and with less depth, the selection of attribute that are not updated is split in two functions depending on the fact that we are using mainly replPropertyMetadata to make our choice or if we are using the list of attributes that should, could or shouldn't be updated/created/deleted. --- source4/scripting/bin/upgradeprovision | 301 +++++++++++++++++++-------------- 1 file changed, 171 insertions(+), 130 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index c625625c44..db42543723 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -774,6 +774,167 @@ msg_elt_flag_strs = { ldb.FLAG_MOD_REPLACE: "MOD_REPLACE", ldb.FLAG_MOD_DELETE: "MOD_DELETE" } +def checkKeepAttributeOldMtd(delta, att, reference, current, + basedn, samdb): + """ Check if we should keep the attribute modification or not. + This function didn't use replicationMetadata to take a decision. + + :param delta: A message diff object + :param att: An attribute + :param reference: A message object for the current entry comming from + the reference provision. + :param current: A message object for the current entry commin from + the current provision. + :param basedn: The DN of the partition + :param samdb: A ldb connection to the sam database of the current provision. + + :return: The modified message diff. + """ + # Old school way of handling things for pre alpha12 upgrade + global defSDmodified + isFirst = False + txt = "" + dn = current[0].dn + + for att in list(delta): + defSDmodified = True + msgElt = delta.get(att) + + if att == "nTSecurityDescriptor": + delta.remove(att) + continue + + if att == "dn": + continue + + if not hashOverwrittenAtt.has_key(att): + if msgElt.flags() != FLAG_MOD_ADD: + if not handle_special_case(att, delta, reference, current, + False, basedn, samdb): + if opts.debugchange or opts.debugall: + try: + dump_denied_change(dn, att, + msg_elt_flag_strs[msgElt.flags()], + current[0][att], reference[0][att]) + except KeyError: + dump_denied_change(dn, att, + msg_elt_flag_strs[msgElt.flags()], + current[0][att], None) + delta.remove(att) + continue + else: + if hashOverwrittenAtt.get(att)&2**msgElt.flags() : + continue + elif hashOverwrittenAtt.get(att)==never: + delta.remove(att) + continue + + return delta + +def checkKeepAttributeWithMetadata(delta, att, message, reference, current, + hash_attr_usn, basedn, usns, samdb): + """ Check if we should keep the attribute modification or not + + :param delta: A message diff object + :param att: An attribute + :param message: A function to print messages + :param reference: A message object for the current entry comming from + the reference provision. + :param current: A message object for the current entry commin from + the current provision. + :param hash_attr_usn: A dictionnary with attribute name as keys, + USN and invocation id as values. + :param basedn: The DN of the partition + :param usns: A dictionnary with invocation ID as keys and USN ranges + as values. + :param samdb: A ldb object pointing to the sam DB + + :return: The modified message diff. + """ + global defSDmodified + isFirst = False + txt = "" + dn = current[0].dn + + for att in list(delta): + # We have updated by provision usn information so let's exploit + # replMetadataProperties + if att in forwardlinked: + curval = current[0].get(att, ()) + refval = reference[0].get(att, ()) + handle_links(samdb, att, basedn, current[0]["dn"], + curval, refval, delta) + continue + + if isFirst and len(delta.items())>1: + isFirst = True + txt = "%s\n" % (str(dn)) + + keptAttr = ["dn", "rIDAvailablePool", "objectSid", "creationTime", "oEMInformation", "msDs-KeyVersionNumber"] + if att in keptAttr: + delta.remove(att) + continue + + if handle_special_case(att, delta, reference, current, True, None, None): + # This attribute is "complicated" to handle and handling + # was done in handle_special_case + continue + + attrUSN = None + if hash_attr_usn.get(att): + attrUSN = hash_attr_usn.get(att) + + if att == "forceLogoff" and attrUSN is None: + continue + if attrUSN is None: + delta.remove(att) + continue + if att == "nTSecurityDescriptor": + cursd = ndr_unpack(security.descriptor, + str(current[0]["nTSecurityDescriptor"])) + cursddl = cursd.as_sddl(names.domainsid) + refsd = ndr_unpack(security.descriptor, + str(reference[0]["nTSecurityDescriptor"])) + refsddl = refsd.as_sddl(names.domainsid) + + if get_diff_sddls(refsddl, cursddl) == "": + message(CHANGE, "sd are identical") + else: + message(CHANGE, "sd are not identical") + if attrUSN == -1: + # This attribute was last modified by another DC forget + # about it + message(CHANGE, "%sAttribute: %s has been " + "created/modified/deleted by another DC. " + "Doing nothing" % (txt, att)) + txt = "" + delta.remove(att) + continue + elif not usn_in_range(int(attrUSN), usns.get(attInvId)): + message(CHANGE, "%sAttribute: %s was not " + "created/modified/deleted during a " + "provision or upgradeprovision. Current " + "usn: %d. Doing nothing" % (txt, att, + attrUSN)) + txt = "" + delta.remove(att) + continue + else: + if att == "defaultSecurityDescriptor": + defSDmodified = True + if attrUSN: + message(CHANGE, "%sAttribute: %s will be modified" + "/deleted it was last modified " + "during a provision. Current usn: " + "%d" % (txt, att, attrUSN)) + txt = "" + else: + message(CHANGE, "%sAttribute: %s will be added because " + "it did not exist before" % (txt, att)) + txt = "" + continue + + return delta def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): """ This function updates the object that are already present in the @@ -788,7 +949,6 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): upgradeprovision :param invocationid: The value of the invocationid for the current DC""" - global defSDmodified # This hash is meant to speedup lookup of attribute name from an oid, # it's for the replPropertyMetaData handling hash_oid_name = {} @@ -805,6 +965,8 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): changed = 0 controls = ["search_options:1:2", "sd_flags:1:2"] + if usns is not None: + message(CHANGE, "Using replPropertyMetadata for change selection") for dn in listPresent: reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn, scope=SCOPE_SUBTREE, @@ -826,9 +988,6 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): delta = samdb.msg_diff(current[0], reference[0]) - for att in hashAttrNotCopied.keys(): - delta.remove(att) - for att in backlinked: delta.remove(att) @@ -851,133 +1010,15 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): # Note we could just use 1 here hash_attr_usn[att] = o.originating_usn else: - hash_attr_usn[att] = -1 - - isFirst = 0 - txt = "" - - for att in list(delta): - if usns is not None: - # We have updated by provision usn information so let's exploit - # replMetadataProperties - if att in forwardlinked: - curval = current[0].get(att, ()) - refval = reference[0].get(att, ()) - handle_links(samdb, att, basedn, current[0]["dn"], - curval, refval, delta) - continue - - if isFirst == 0 and len(delta.items())>1: - isFirst = 1 - txt = "%s\n" % (str(dn)) - if att == "dn": - # There is always a dn attribute after a msg_diff - continue - if att == "rIDAvailablePool": - delta.remove(att) - continue - if att == "objectSid": - delta.remove(att) - continue - if att == "creationTime": - delta.remove(att) - continue - if att == "oEMInformation": - delta.remove(att) - continue - if att == "msDs-KeyVersionNumber": - # This is the kvno of the computer/user it's a very bad - # idea to change it - delta.remove(att) - continue - if handle_special_case(att, delta, reference, current, True, basedn, samdb): - # This attribute is "complicated" to handle and handling - # was done in handle_special_case - continue - attrUSN = hash_attr_usn.get(att) - if att == "forceLogoff" and attrUSN is None: - continue - if attrUSN is None: - delta.remove(att) - continue - if att == "nTSecurityDescriptor": - cursd = ndr_unpack(security.descriptor, - str(current[0]["nTSecurityDescriptor"])) - cursddl = cursd.as_sddl(names.domainsid) - refsd = ndr_unpack(security.descriptor, - str(reference[0]["nTSecurityDescriptor"])) - refsddl = refsd.as_sddl(names.domainsid) - - if get_diff_sddls(refsddl, cursddl) == "": - message(CHANGE, "sd are identical") - else: - message(CHANGE, "sd are not identical") - if attrUSN == -1: - # This attribute was last modified by another DC forget - # about it - message(CHANGE, "%sAttribute: %s has been " - "created/modified/deleted by another DC. " - "Doing nothing" % (txt, att)) - txt = "" - delta.remove(att) - continue - elif not usn_in_range(int(attrUSN), usns): - message(CHANGE, "%sAttribute: %s was not " - "created/modified/deleted during a " - "provision or upgradeprovision. Current " - "usn: %d. Doing nothing" % (txt, att, - attrUSN)) - txt = "" - delta.remove(att) - continue - else: - if att == "defaultSecurityDescriptor": - defSDmodified = True - if attrUSN: - message(CHANGE, "%sAttribute: %s will be modified" - "/deleted it was last modified " - "during a provision. Current usn: " - "%d" % (txt, att, attrUSN)) - txt = "" - else: - message(CHANGE, "%sAttribute: %s will be added because " - "it did not exist before" % (txt, att)) - txt = "" - continue - - else: - # Old school way of handling things for pre alpha12 upgrade - defSDmodified = True - msgElt = delta.get(att) - if att == "nTSecurityDescriptor": - delta.remove(att) - continue - - if att == "dn": - continue - - if not hashOverwrittenAtt.has_key(att): - if msgElt.flags() != FLAG_MOD_ADD: - if not handle_special_case(att, delta, reference, current, - False, basedn, samdb): - if opts.debugchange or opts.debugall: - try: - dump_denied_change(dn, att, - msg_elt_flag_strs[msgElt.flags()], - current[0][att], reference[0][att]) - except KeyError: - dump_denied_change(dn, att, - msg_elt_flag_strs[msgElt.flags()], - current[0][att], None) - delta.remove(att) - continue - else: - if hashOverwrittenAtt.get(att)&2**msgElt.flags() : - continue - elif hashOverwrittenAtt.get(att)==never: - delta.remove(att) - continue + if usns is not None: + delta = checkKeepAttributeWithMetadata(delta, att, message, reference, + current, hash_attr_usn, + basedn, usns, samdb) + else: + for att in hashAttrNotCopied.keys(): + delta.remove(att) + delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb) delta.dn = dn if len(delta.items()) >1: -- cgit From 71ab462c81b48169b34d60dd2bbeca137a15b702 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 18:34:49 +0400 Subject: s4-upgradeprovision: add function to know if attribute is replicated or not --- source4/scripting/bin/upgradeprovision | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index db42543723..a9dffc0ab5 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -117,6 +117,7 @@ hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace, backlinked = [] forwardlinked = set() dn_syntax_att = [] +not_replicated = [] def define_what_to_log(opts): what = 0 if opts.debugchange: @@ -238,6 +239,25 @@ def populate_links(samdb, schemadn): for t in linkedAttHash.keys(): forwardlinked.add(t) +def isReplicated(att): + """ Indicate if the attribute is replicated or not + + :param att: Name of the attribute to be tested + :return: True is the attribute is replicated, False otherwise + """ + + return (att not in not_replicated) + +def populateNotReplicated(samdb, schemadn): + """Populate an array with all the attributes that are not replicated + + :param samdb: A LDB object for sam.ldb file + :param schemadn: DN of the schema for the partition""" + res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb, + str(schemadn)), scope=SCOPE_SUBTREE, + attrs=["lDAPDisplayName"]) + for elem in res: + not_replicated.append(elem["lDAPDisplayName"]) def populate_dnsyntax(samdb, schemadn): """Populate an array with all the attributes that have DN synthax -- cgit From 20233cdf535b55ee3832d4844eb2109cccab5837 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 17:39:06 +0400 Subject: s4-upgradeprovision: introduce invocation id in lastprovisionUSNs --- source4/scripting/bin/upgradeprovision | 46 +++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index a9dffc0ab5..7f11e404ab 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -902,10 +902,22 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, attrUSN = None if hash_attr_usn.get(att): - attrUSN = hash_attr_usn.get(att) + [attrUSN, attInvId] = hash_attr_usn.get(att) + + if attrUSN is None: + # If it's a replicated attribute and we don't have any USN + # information about it. It means that we never saw it before + # so let's add it ! + # If it is a replicated attribute but we are not master on it + # (ie. not initially added in the provision we masterize). + # attrUSN will be -1 + if isReplicated(att): + continue + elif att in hashAttrNotCopied.keys(): + delta.remove(att) + else: + continue - if att == "forceLogoff" and attrUSN is None: - continue if attrUSN is None: delta.remove(att) continue @@ -956,7 +968,7 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, return delta -def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): +def update_present(ref_samdb, samdb, basedn, listPresent, usns): """ This function updates the object that are already present in the provision @@ -966,8 +978,8 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): (ie. DC=foo, DC=bar) :param listPresent: A list of object that is present in the provision :param usns: A list of USN range modified by previous provision and - upgradeprovision - :param invocationid: The value of the invocationid for the current DC""" + upgradeprovision grouped by invocation ID + """ # This hash is meant to speedup lookup of attribute name from an oid, # it's for the replPropertyMetaData handling @@ -1026,10 +1038,10 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): # We put in this hash only modification # made on the current host att = hash_oid_name[samdb.get_oid_from_attid(o.attid)] - if str(o.originating_invocation_id) == str(invocationid): - # Note we could just use 1 here - hash_attr_usn[att] = o.originating_usn + if str(o.originating_invocation_id) in usns.keys(): + hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)] else: + hash_attr_usn[att] = [-1, None] if usns is not None: delta = checkKeepAttributeWithMetadata(delta, att, message, reference, @@ -1160,7 +1172,7 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre message(SIMPLE, "Schema reloaded!") changed = update_present(ref_samdb, samdb, basedn, listPresent, - provisionUSNs, names.invocation) + provisionUSNs) message(SIMPLE, "There are %d changed objects" % (changed)) return 1 @@ -1665,9 +1677,19 @@ if __name__ == '__main__': # 4) lastProvisionUSNs = get_last_provision_usn(ldbs.sam) if lastProvisionUSNs is not None: + v = 0 + for k in lastProvisionUSNs.keys(): + for r in lastProvisionUSNs[k]: + v = v + 1 + message(CHANGE, - "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs)) + "Find last provision USN, %d invocation(s) for a total of %d ranges" % \ + (len(lastProvisionUSNs.keys()), v /2 )) + if lastProvisionUSNs.get("default") != None: + message(CHANGE, "Old style for usn ranges used") + lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"] + del lastProvisionUSNs["default"] # Objects will be created with the admin session # (not anymore system session) adm_session = admin_session(lp, str(names.domainsid)) @@ -1853,7 +1875,7 @@ if __name__ == '__main__': check_for_DNS(newpaths.private_dir, paths.private_dir) # 22) if lastProvisionUSNs is not None: - update_provision_usn(ldbs.sam, minUSN, maxUSN) + 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 or opts.resetfileacl or opts.fixntacl: -- cgit From 9a18e07b4f186751cb25dfc0f947e6e5ca8f2fee Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 17:56:17 +0400 Subject: s4-upgradeprovision: clean up, reformating and update docs --- source4/scripting/bin/upgradeprovision | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 7f11e404ab..85f21678d6 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -412,14 +412,14 @@ def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb): if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE): hash = {} newval = [] - changeDelta=0 + changeDelta = 0 for elem in old[0][att]: hash[str(elem)]=1 newval.append(str(elem)) for elem in new[0][att]: if not hash.has_key(str(elem)): - changeDelta=1 + changeDelta = 1 newval.append(str(elem)) if changeDelta == 1: delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att) @@ -1109,8 +1109,8 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre :param basedn: String value of the DN of the partition :param names: List of key provision parameters :param schema: A Schema object - :param provisionUSNs: The USNs modified by provision/upgradeprovision - last time + :param provisionUSNs: A dictionnary with range of USN modified during provision + or upgradeprovision. Ranges are grouped by invocationID. :param prereloadfunc: A function that must be executed just before the reload of the schema """ @@ -1309,7 +1309,6 @@ def removeProvisionUSN(samdb): attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"] entry = samdb.search(expression="dn=@PROVISION", base = "", scope=SCOPE_SUBTREE, - controls=["search_options:1:2"], attrs=attrs) empty = Message() empty.dn = entry[0].dn @@ -1379,7 +1378,7 @@ def update_privilege(ref_private_path, cur_private_path): os.path.join(cur_private_path, "privilege.ldb")) -def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc): +def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc): """Upgrade the SAM DB contents for all the provision partitions :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference @@ -1387,8 +1386,8 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc): :param samdb: An LDB object connected to the sam.ldb of the update provision :param names: List of key provision parameters - :param highestUSN: The highest USN modified by provision/upgradeprovision - last time + :param provisionUSNs: A dictionnary with range of USN modified during provision + or upgradeprovision. Ranges are grouped by invocationID. :param schema: A Schema object that represent the schema of the provision :param prereloadfunc: A function that must be executed just before the reload of the schema @@ -1396,7 +1395,7 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc): message(SIMPLE, "Starting update of samdb") ret = update_partition(ref_samdb, samdb, str(names.rootdn), names, - schema, highestUSN, prereloadfunc) + schema, provisionUSNs, prereloadfunc) if ret: message(SIMPLE, "Update of samdb finished") return 1 -- cgit From 44c540625216b7f9754f7e9461bbd5d026a1e9cf Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 17:15:37 +0400 Subject: s4-upgradeprovision: don't print dn in the list of modified attributes --- source4/scripting/bin/upgradeprovision | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 85f21678d6..b197c6cb84 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -1054,7 +1054,8 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns): delta.dn = dn if len(delta.items()) >1: - attributes=", ".join(delta.keys()) + # Skip dn as the value is not really changed ... + attributes=", ".join(delta.keys()[1:]) modcontrols = [] relaxedatt = ['iscriticalsystemobject', 'grouptype'] # Let's try to reduce as much as possible the use of relax control -- cgit From 01758595e3c01751f259e5b2edaf07beb982ed76 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 18:37:51 +0400 Subject: s4-upgradeprovision: remove useless code --- source4/scripting/bin/upgradeprovision | 3 --- 1 file changed, 3 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index b197c6cb84..735757aa2c 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -918,9 +918,6 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, else: continue - if attrUSN is None: - delta.remove(att) - continue if att == "nTSecurityDescriptor": cursd = ndr_unpack(security.descriptor, str(current[0]["nTSecurityDescriptor"])) -- cgit From 4305f54b8ef9fdcc1ca075b991e9dadab4b485c7 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 00:02:03 +0400 Subject: s4-upgradeprovision: fix inverted logic and wrong flags on sd_flags control --- source4/scripting/bin/upgradeprovision | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 735757aa2c..b49ccf91c0 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -872,7 +872,7 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, :return: The modified message diff. """ global defSDmodified - isFirst = False + isFirst = True txt = "" dn = current[0].dn @@ -887,7 +887,7 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, continue if isFirst and len(delta.items())>1: - isFirst = True + isFirst = False txt = "%s\n" % (str(dn)) keptAttr = ["dn", "rIDAvailablePool", "objectSid", "creationTime", "oEMInformation", "msDs-KeyVersionNumber"] @@ -930,6 +930,7 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, message(CHANGE, "sd are identical") else: message(CHANGE, "sd are not identical") + if attrUSN == -1: # This attribute was last modified by another DC forget # about it @@ -993,7 +994,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns): raise ProvisioningError(msg) changed = 0 - controls = ["search_options:1:2", "sd_flags:1:2"] + controls = ["search_options:1:2", "sd_flags:1:0"] if usns is not None: message(CHANGE, "Using replPropertyMetadata for change selection") for dn in listPresent: -- cgit From f76c206e2fd9af47767816e6b284e3742672b21a Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 18:49:23 +0400 Subject: s4-upgradeprovision: change hashAttrNotCopied to be an array --- source4/scripting/bin/upgradeprovision | 48 ++++++++++++++++------------------ 1 file changed, 22 insertions(+), 26 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index b49ccf91c0..c353fe0749 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -81,20 +81,21 @@ __docformat__ = "restructuredText" # This is most probably because they are populated automatcally when object is # created # This also apply to imported object from reference provision -hashAttrNotCopied = { "dn": 1, "whenCreated": 1, "whenChanged": 1, - "objectGUID": 1, "uSNCreated": 1, - "replPropertyMetaData": 1, "uSNChanged": 1, - "parentGUID": 1, "objectCategory": 1, - "distinguishedName": 1, "nTMixedDomain": 1, - "showInAdvancedViewOnly": 1, "instanceType": 1, - "msDS-Behavior-Version":1, "nextRid":1, "cn": 1, - "lmPwdHistory":1, "pwdLastSet": 1, - "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1, - "supplementalCredentials":1, "gPCUserExtensionNames":1, - "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1, - "possibleInferiors":1, "privilege":1, - "sAMAccountType":1 } - +replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID", + "parentGUID", "objectCategory", "distinguishedName", + "nTMixedDomain", "showInAdvancedViewOnly", + "instanceType", "msDS-Behavior-Version", "cn", + "lmPwdHistory", "pwdLastSet", "ntPwdHistory", + "unicodePwd", "dBCSPwd", "supplementalCredentials", + "gPCUserExtensionNames", "gPCMachineExtensionNames", + "maxPwdAge", "secret", "possibleInferiors", "privilege", + "sAMAccountType", "oEMInformation", "creationTime" ] + +nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged", + "nextRid" ,"rIDNextRID"] + +attrNotCopied = replAttrNotCopied +attrNotCopied.extend(nonreplAttrNotCopied) # Usually for an object that already exists we do not overwrite attributes as # they might have been changed for good reasons. Anyway for a few of them it's # mandatory to replace them otherwise the provision will be broken somehow. @@ -606,7 +607,7 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index): m = re.match(r".*-(\d+)$", sid) if m and int(m.group(1))>999: delta.remove("objectSid") - for att in hashAttrNotCopied.keys(): + for att in attrNotCopied: delta.remove(att) for att in backlinked: delta.remove(att) @@ -673,7 +674,7 @@ def add_deletedobj_containers(ref_samdb, samdb, names): delta = samdb.msg_diff(empty, reference[0]) delta.dn = Dn(samdb, str(reference[0]["dn"])) - for att in hashAttrNotCopied.keys(): + for att in attrNotCopied: delta.remove(att) modcontrols = ["relax:0", "provision:0"] @@ -890,11 +891,6 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, isFirst = False txt = "%s\n" % (str(dn)) - keptAttr = ["dn", "rIDAvailablePool", "objectSid", "creationTime", "oEMInformation", "msDs-KeyVersionNumber"] - if att in keptAttr: - delta.remove(att) - continue - if handle_special_case(att, delta, reference, current, True, None, None): # This attribute is "complicated" to handle and handling # was done in handle_special_case @@ -913,9 +909,8 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, # attrUSN will be -1 if isReplicated(att): continue - elif att in hashAttrNotCopied.keys(): - delta.remove(att) else: + message(CHANGE, "Non replicated attribute %s changed" % att) continue if att == "nTSecurityDescriptor": @@ -1021,6 +1016,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns): for att in backlinked: delta.remove(att) + for att in attrNotCopied: + delta.remove(att) + delta.remove("name") if len(delta.items()) > 1 and usns is not None: @@ -1046,8 +1044,6 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns): current, hash_attr_usn, basedn, usns, samdb) else: - for att in hashAttrNotCopied.keys(): - delta.remove(att) delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb) delta.dn = dn @@ -1611,7 +1607,7 @@ def sync_calculated_attributes(samdb, names): # This resulting object is filtered to remove all the back link attribute # (ie. memberOf) as they will be created by the other linked object (ie. # the one with the member attribute) -# All attributes specified in the hashAttrNotCopied associative array are +# All attributes specified in the attrNotCopied array are # also removed it's most of the time generated attributes # After missing entries have been added the update_partition function will -- cgit From 0e729149259055e9310c1dee78fce71a744006ab Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 22:59:35 +0400 Subject: s4-upgradeprovision: dn must be skipped as delta.remove("dn") do not remove this attribute --- source4/scripting/bin/upgradeprovision | 3 +++ 1 file changed, 3 insertions(+) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index c353fe0749..79cf97dce4 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -878,6 +878,9 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, dn = current[0].dn for att in list(delta): + if att == "dn": + # dn is not removable + continue # We have updated by provision usn information so let's exploit # replMetadataProperties if att in forwardlinked: -- cgit From 930d2f28c99d5cf8823ca0837a632e13cead9fce Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 23:23:05 +0400 Subject: s4-upgradeprovision: if there is nothing to really modify then skip it --- source4/scripting/bin/upgradeprovision | 3 +++ 1 file changed, 3 insertions(+) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 79cf97dce4..b214c4c8fd 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -1024,6 +1024,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns): delta.remove("name") + if len(delta.items()) == 1: + continue + if len(delta.items()) > 1 and usns is not None: # Fetch the replPropertyMetaData res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn, -- cgit From f7a903ee8085bb041cae8fdf603997e66245f35f Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 14 Jun 2011 01:39:41 +0400 Subject: s4-upgradeprovision: add a list of attribute that are not DSDB attribute that we don't want to copy --- source4/scripting/bin/upgradeprovision | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index b214c4c8fd..e2d5cb0fb8 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -94,8 +94,12 @@ replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID", nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged", "nextRid" ,"rIDNextRID"] +nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"] + + attrNotCopied = replAttrNotCopied attrNotCopied.extend(nonreplAttrNotCopied) +attrNotCopied.extend(nonDSDBAttrNotCopied) # Usually for an object that already exists we do not overwrite attributes as # they might have been changed for good reasons. Anyway for a few of them it's # mandatory to replace them otherwise the provision will be broken somehow. -- cgit From bc7b8fa108bf27f78c69f5aec3e408e59555c232 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 14 Jun 2011 01:41:56 +0400 Subject: s4-upgradeprovision: ignore objectSid --- source4/scripting/bin/upgradeprovision | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index e2d5cb0fb8..bf06a3c4f7 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -882,9 +882,10 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, dn = current[0].dn for att in list(delta): - if att == "dn": - # dn is not removable + if att in ["dn", "objectSid"]: + delta.remove(att) continue + # We have updated by provision usn information so let's exploit # replMetadataProperties if att in forwardlinked: -- cgit From c0eb4037585e1feb609d7acef196c4dc8872960b Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 14 Jun 2011 01:42:28 +0400 Subject: s4-upgradeprovision: remove useless comment --- source4/scripting/bin/upgradeprovision | 1 - 1 file changed, 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index bf06a3c4f7..a5a42a9d46 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -1064,7 +1064,6 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns): modcontrols = [] relaxedatt = ['iscriticalsystemobject', 'grouptype'] # Let's try to reduce as much as possible the use of relax control - #for checkedatt in relaxedatt: for attr in delta.keys(): if attr.lower() in relaxedatt: modcontrols = ["relax:0", "provision:0"] -- cgit From 5e81ee8b341c3c6a6f9a321ec6ddf9b29932b683 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 14 Jun 2011 01:42:59 +0400 Subject: s4-upgradeprovision: Rework completly how SDs are recalculated --- source4/scripting/bin/upgradeprovision | 165 ++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 66 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index a5a42a9d46..284b0e0ef8 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -42,7 +42,7 @@ from samba.credentials import DONT_USE_KERBEROS from samba.auth import system_session, admin_session from ldb import (SCOPE_SUBTREE, SCOPE_BASE, FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE, - MessageElement, Message, Dn) + MessageElement, Message, Dn, LdbError) from samba import param, dsdb, Ldb from samba.provision import (get_domain_descriptor, find_provision_key_parameters, get_config_descriptor, @@ -119,6 +119,8 @@ hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace, "attributeDisplayNames": replace + add, "versionNumber": add} +dnNotToRecalculate = [] +dnToRecalculate = [] backlinked = [] forwardlinked = set() dn_syntax_att = [] @@ -822,10 +824,10 @@ def checkKeepAttributeOldMtd(delta, att, reference, current, dn = current[0].dn for att in list(delta): - defSDmodified = True msgElt = delta.get(att) if att == "nTSecurityDescriptor": + defSDmodified = True delta.remove(att) continue @@ -929,10 +931,25 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current, str(reference[0]["nTSecurityDescriptor"])) refsddl = refsd.as_sddl(names.domainsid) - if get_diff_sddls(refsddl, cursddl) == "": - message(CHANGE, "sd are identical") + diff = get_diff_sddls(refsddl, cursddl) + if diff == "": + # FIXME find a way to have it only with huge huge verbose mode + # message(CHANGE, "%ssd are identical" % txt) + # txt = "" + delta.remove(att) + continue else: - message(CHANGE, "sd are not identical") + delta.remove(att) + message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff)) + txt = "" + if attrUSN == -1: + 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)) + else: + dnToRecalculate.append(str(dn)) + continue if attrUSN == -1: # This attribute was last modified by another DC forget @@ -1219,7 +1236,7 @@ def check_updated_sd(ref_sam, cur_sam, names): str(current[i]["nTSecurityDescriptor"])) sddl = cursd.as_sddl(names.domainsid) if sddl != hash[key]: - txt = get_diff_sddls(hash[key], sddl) + txt = get_diff_sddls(hash[key], sddl, False) if txt != "": message(CHANGESD, "On object %s ACL is different" " \n%s" % (current[i]["dn"], txt)) @@ -1233,37 +1250,38 @@ def fix_partition_sd(samdb, names): :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 + + + # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate # First update the SD for the rootdn - res = samdb.search(expression="objectClass=*", base=str(names.rootdn), - scope=SCOPE_BASE, attrs=["dn", "whenCreated"], - controls=["search_options:1:2"]) - delta = Message() - delta.dn = Dn(samdb, str(res[0]["dn"])) - descr = get_domain_descriptor(names.domainsid) - delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, - "nTSecurityDescriptor") - samdb.modify(delta) + if alwaysRecalculate or str(names.rootdn) in dnToRecalculate: + delta = Message() + delta.dn = Dn(samdb, str(names.rootdn)) + descr = get_domain_descriptor(names.domainsid) + delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, + "nTSecurityDescriptor") + samdb.modify(delta) + # Then the config dn - res = samdb.search(expression="objectClass=*", base=str(names.configdn), - scope=SCOPE_BASE, attrs=["dn", "whenCreated"], - controls=["search_options:1:2"]) - delta = Message() - delta.dn = Dn(samdb, str(res[0]["dn"])) - descr = get_config_descriptor(names.domainsid) - delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, - "nTSecurityDescriptor" ) - samdb.modify(delta) - # Then the schema dn - res = samdb.search(expression="objectClass=*", base=str(names.schemadn), - scope=SCOPE_BASE, attrs=["dn", "whenCreated"], - controls=["search_options:1:2"]) + if alwaysRecalculate or str(names.configdn) in dnToRecalculate: + delta = Message() + delta.dn = Dn(samdb, str(names.configdn)) + descr = get_config_descriptor(names.domainsid) + delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, + "nTSecurityDescriptor" ) + samdb.modify(delta) - delta = Message() - delta.dn = Dn(samdb, str(res[0]["dn"])) - descr = get_schema_descriptor(names.domainsid) - delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, - "nTSecurityDescriptor" ) - samdb.modify(delta) + # Then the schema dn + if alwaysRecalculate or str(names.schemadn) in dnToRecalculate: + delta = Message() + delta.dn = Dn(samdb, str(names.schemadn)) + descr = get_schema_descriptor(names.domainsid) + delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, + "nTSecurityDescriptor" ) + samdb.modify(delta) def rebuild_sd(samdb, names): """Rebuild security descriptor of the current provision from scratch @@ -1276,30 +1294,46 @@ def rebuild_sd(samdb, names): :param names: List of key provision parameters""" + fix_partition_sd(samdb, names) + # List of namming contexts + listNC = [str(names.rootdn), str(names.configdn), str(names.schemadn)] hash = {} - res = samdb.search(expression="objectClass=*", base=str(names.rootdn), + 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: - if not (str(obj["dn"]) == str(names.rootdn) or - str(obj["dn"]) == str(names.configdn) or - str(obj["dn"]) == str(names.schemadn)): - hash[str(obj["dn"])] = obj["whenCreated"] - - listkeys = hash.keys() - listkeys.sort(dn_sort) - - for key in listkeys: + 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"] + + listKeys = list(set(hash.keys())) + listKeys.sort(dn_sort) + + 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))) + + for key in listKeys: + if (key in listNC or + key in dnNotToRecalculate): + continue + delta = Message() + delta.dn = Dn(samdb, key) try: - delta = Message() - delta.dn = Dn(samdb, key) delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE, "whenCreated" ) - samdb.modify(delta, ["recalculate_sd:0"]) - except: - # XXX: We should always catch an explicit exception. - # What could go wrong here? + samdb.modify(delta, ["recalculate_sd:0","relax:0"]) + except LdbError, e: samdb.transaction_cancel() res = samdb.search(expression="objectClass=*", base=str(names.rootdn), scope=SCOPE_SUBTREE, @@ -1307,7 +1341,7 @@ def rebuild_sd(samdb, names): controls=["search_options:1:2"]) badsd = ndr_unpack(security.descriptor, str(res[0]["nTSecurityDescriptor"])) - print "bad stuff %s" % badsd.as_sddl(names.domainsid) + message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid))) return def removeProvisionUSN(samdb): @@ -1838,12 +1872,12 @@ if __name__ == '__main__': 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 not re.match(r'.*alpha(9|\d\d+)', str(oem)): - message(SIMPLE, "Fixing old povision SD") - fix_partition_sd(ldbs.sam, names) + 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 @@ -1854,23 +1888,22 @@ if __name__ == '__main__': # 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") + # 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 defaultSecurityDescriptors and/or" + "securityDescriptor have changed, recalculating 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) + rebuild_sd(ldbs.sam, names) # 19) # Now we are quite confident in the recalculate process of the SD, we make - # it optional. + # 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 opts.debugchangesd: + 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) -- cgit From 05b2d4147a75a652f8f773d353b62a4c10821155 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Wed, 15 Jun 2011 15:20:46 +0400 Subject: s4-upgradeprovision: deltaattr can be empty or none too --- source4/scripting/bin/upgradeprovision | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 284b0e0ef8..9bf09bd432 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -1808,14 +1808,14 @@ if __name__ == '__main__': 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 + 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") -- cgit From 8a19e1ecfb716579e662d2d6b6bceeccbf7e7741 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 01:17:27 +0400 Subject: s4-upgradeprovision: skip versionNumber, it's used by GPO --- source4/scripting/bin/upgradeprovision | 1 + 1 file changed, 1 insertion(+) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 9bf09bd432..45c14e0027 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -113,6 +113,7 @@ hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace, "wellKnownObjects":replace, "privilege":never, "defaultSecurityDescriptor": replace, "rIDAvailablePool": never, + "versionNumber" : add, "rIDNextRID": add, "rIDUsedPool": never, "defaultSecurityDescriptor": replace + add, "isMemberOfPartialAttributeSet": delete, -- cgit From 957b1ff183cf713bebc4cc9a32cabacc1e86b13e Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 20 Jun 2011 01:00:48 +0400 Subject: s4: create script to find provision ranges for old provision without this information --- source4/scripting/bin/findprovisionusnranges | 174 +++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100755 source4/scripting/bin/findprovisionusnranges (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/findprovisionusnranges b/source4/scripting/bin/findprovisionusnranges new file mode 100755 index 0000000000..c91e42e936 --- /dev/null +++ b/source4/scripting/bin/findprovisionusnranges @@ -0,0 +1,174 @@ +#!/usr/bin/python +# +# Helper for determining USN ranges created of modified by provision and +# upgradeprovision. +# Copyright (C) Matthieu Patou 2009-2011 +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import sys +import optparse +import tempfile +sys.path.insert(0, "bin/python") + +from samba.credentials import DONT_USE_KERBEROS +from samba.auth import system_session +from samba import Ldb +import ldb + +import samba.getopt as options +from samba import param +from samba import _glue +from samba.upgradehelpers import get_paths +from samba.ndr import ndr_unpack +from samba.dcerpc import drsblobs, misc + +parser = optparse.OptionParser("provision [options]") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +parser.add_option("--storedir", type="string", help="Directory where to store result files") +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +opts = parser.parse_args()[0] +lp = sambaopts.get_loadparm() +smbconf = lp.configfile + +creds = credopts.get_credentials(lp) +creds.set_kerberos_state(DONT_USE_KERBEROS) +session = system_session() +paths = get_paths(param, smbconf=smbconf) +basedn="DC=" + lp.get("realm").replace(".",",DC=") +samdb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp) + +hash_id = {} +ldif = "" +nb_obj = 0 + +res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) + +invocation = None +if res and len(res) == 1 and res[0]["dsServiceName"] != None: + dn = ldb.Dn(samdb, str(res[0]["dsServiceName"])) + res = samdb.search(base=str(dn), scope=ldb.SCOPE_BASE, attrs=["invocationId"], + controls=["search_options:1:2"]) + + if res and len(res) == 1 and res[0]["invocationId"]: + invocation = str(ndr_unpack(misc.GUID, res[0]["invocationId"][0])) + else: + print "Unable to find invocation ID" + sys.exit(1) +else: + print "Unable to find attribute dsServiceName in rootDSE" + sys.exit(1) + +res = samdb.search(base=basedn, expression="objectClass=*", + scope=ldb.SCOPE_SUBTREE, + attrs=["replPropertyMetaData"], + controls=["search_options:1:2"]) + +for e in res: + nb_obj = nb_obj + 1 + obj = ndr_unpack(drsblobs.replPropertyMetaDataBlob, + str(e["replPropertyMetaData"])).ctr + + for o in obj.array: + # like a timestamp but with the resolution of 1 minute + minutestamp =_glue.nttime2unix(o.originating_change_time)/60 + hash_ts = hash_id.get(str(o.originating_invocation_id)) + if hash_ts == None: + ob = {} + ob["min"] = o.originating_usn + ob["max"] = o.originating_usn + ob["num"] = 1 + ob["list"] = [str(e.dn)] + hash_ts = {} + else: + ob = hash_ts.get(minutestamp) + if ob == None: + ob = {} + ob["min"] = o.originating_usn + ob["max"] = o.originating_usn + ob["num"] = 1 + ob["list"] = [str(e.dn)] + else: + if ob["min"] > o.originating_usn: + ob["min"] = o.originating_usn + if ob["max"] < o.originating_usn: + ob["max"] = o.originating_usn + if not (str(e.dn) in ob["list"]): + ob["num"] = ob["num"] + 1 + ob["list"].append(str(e.dn)) + hash_ts[minutestamp] = ob + hash_id[str(o.originating_invocation_id)] = hash_ts + +minobj = 5 +print "Here is a list of changes that modified more than %d objects in 1 minute." % minobj +print "Usually changes made by provision and upgradeprovision are those who affect a couple"\ + " of hundred of objects or more" +print "Total number of objects: %d" % nb_obj +print + +for id in hash_id: + hash_ts = hash_id[id] + sorted_keys = [] + sorted_keys.extend(hash_ts.keys()) + sorted_keys.sort() + + kept_record = [] + for k in sorted_keys: + obj = hash_ts[k] + if obj["num"] > minobj: + dt = _glue.nttime2string(_glue.unix2nttime(k*60)) + print "%s # of modification: %d \tmin: %d max: %d" % (dt , obj["num"], + obj["min"], + obj["max"]) + if hash_ts[k]["num"] > 600: + kept_record.append(k) + + # Let's try to concatenate consecutive block if they are in the almost same minutestamp + for i in range(0, len(kept_record)): + if i != 0: + key1 = kept_record[i] + key2 = kept_record[i-1] + if key1 - key2 == 1: + # previous record is just 1 minute away from current + if int(hash_ts[key1]["min"]) == int(hash_ts[key2]["max"]) + 1: + # Copy the highest USN in the previous record + # and mark the current as skipped + hash_ts[key2]["max"] = hash_ts[key1]["max"] + hash_ts[key1]["skipped"] = True + + for k in kept_record: + obj = hash_ts[k] + if obj.get("skipped") == None: + ldif = "%slastProvisionUSN: %d-%d;%s\n" % (ldif, obj["min"], + obj["max"], id) + +if ldif != "": + dest = opts.storedir + if dest == None: + dest = "/tmp" + + file = tempfile.mktemp(dir=dest, prefix="usnprov", suffix=".ldif") + print + print "To track the USNs modified/created by provision and upgrade proivsion," + print " the following ranges are proposed to be added to your provision sam.ldb: \n%s" % ldif + print "We recommend to review them, and if it's correct to integrate the following ldif: %s in your sam.ldb" % file + print "You can load this file like this: ldbadd -H %s %s\n"%(str(paths.samdb),file) + ldif = "dn: @PROVISION\nprovisionnerID: %s\n%s" % (invocation, ldif) + open(file,'w').write(ldif) + -- cgit From 01ce078ed166635c29e89bd012c82e3612393f28 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 20 Jun 2011 01:05:04 +0400 Subject: s4-upgradeprovision: propose the use of findprovisionranges if no ranges are present Autobuild-User: Matthieu Patou Autobuild-Date: Mon Jun 20 00:30:59 CEST 2011 on sn-devel-104 --- source4/scripting/bin/upgradeprovision | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 45c14e0027..f10a9fcc9c 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -44,6 +44,7 @@ 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.provision import (get_domain_descriptor, find_provision_key_parameters, get_config_descriptor, ProvisioningError, get_last_provision_usn, @@ -1729,6 +1730,19 @@ if __name__ == '__main__': message(CHANGE, "Old style for usn ranges used") lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"] del lastProvisionUSNs["default"] + else: + message(SIMPLE, "Your provision lacks provision range information") + if confirm("Do you want to run findprovisionusnranges to try to find them ?", False): + ldbs.groupedRollback() + os.system("%s %s %s %s %s" % (os.path.join(os.path.dirname(sys.argv[0]), + "findprovisionusnranges"), + "--storedir", + paths.private_dir, + "-s", + smbconf)) + message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script") + sys.exit(0) + # Objects will be created with the admin session # (not anymore system session) adm_session = admin_session(lp, str(names.domainsid)) -- cgit From c2dfaa2580918cf31069c1063ff07a819ca0554a Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 21 Jun 2011 13:37:26 +0400 Subject: s4-upgradeprovision: Don't forget to populate the non replicated objects, and don't touch rIDPreviousAllocationPool --- source4/scripting/bin/upgradeprovision | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index f10a9fcc9c..e98b642776 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -93,7 +93,7 @@ replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID", "sAMAccountType", "oEMInformation", "creationTime" ] nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged", - "nextRid" ,"rIDNextRID"] + "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"] nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"] @@ -266,7 +266,8 @@ def populateNotReplicated(samdb, schemadn): str(schemadn)), scope=SCOPE_SUBTREE, attrs=["lDAPDisplayName"]) for elem in res: - not_replicated.append(elem["lDAPDisplayName"]) + not_replicated.append(str(elem["lDAPDisplayName"])) + def populate_dnsyntax(samdb, schemadn): """Populate an array with all the attributes that have DN synthax @@ -1778,6 +1779,7 @@ if __name__ == '__main__': 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) -- cgit From 249fbd8a334b4d19f9148e07449fec3f26b8267d Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 21 Jun 2011 13:39:28 +0400 Subject: s4-samba_dnsupdate: set environment via the env parameter I faced a situation where the os.environ("KRB5CCNAME") = ... didn't seems to be effective --- source4/scripting/bin/samba_dnsupdate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/bin') diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 0a13dd7c9b..78d7dc1712 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -278,7 +278,7 @@ def call_nsupdate(d): try: cmd = nsupdate_cmd[:] cmd.append(tmpfile) - ret = subprocess.call(cmd, shell=False) + ret = subprocess.call(cmd, shell=False, env={"KRB5CCNAME": ccachename}) if ret != 0: if opts.fail_immediately: sys.exit(1) -- cgit