From fd2eb0dfd092e00408f206e6fe7ff302ccd27a10 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 4 Jul 2010 16:38:54 +0400 Subject: s4 provision: move update_machine_account_password to helpers This is to allow reuse of this function and also unit tests Signed-off-by: Andrew Bartlett --- source4/scripting/bin/upgradeprovision | 55 +++--------------------- source4/scripting/python/samba/upgradehelpers.py | 45 ++++++++++++++++++- 2 files changed, 51 insertions(+), 49 deletions(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 48c4ce63b8..0a22a3c747 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -43,20 +43,20 @@ from ldb import (SCOPE_SUBTREE, SCOPE_BASE, MessageElement, Message, Dn) from samba import param from samba.provision import (find_setup_dir, get_domain_descriptor, - get_config_descriptor, secretsdb_self_join, + get_config_descriptor, ProvisioningError, get_last_provision_usn, get_max_usn, update_provision_usn) from samba.schema import get_linked_attributes, Schema, get_schema_descriptor from samba.dcerpc import security, drsblobs from samba.ndr import ndr_unpack -from samba.dcerpc.misc import SEC_CHAN_BDC from samba.upgradehelpers import (dn_sort, get_paths, newprovision, find_provision_key_parameters, get_ldbs, usn_in_range, identic_rename, get_diff_sddls, update_secrets, CHANGE, ERROR, SIMPLE, CHANGEALL, GUESS, CHANGESD, PROVISION, updateOEMInfo, getOEMInfo, update_gpo, - delta_update_basesamdb, update_policyids) + delta_update_basesamdb, update_policyids, + update_machine_account_password) replace=2**FLAG_MOD_REPLACE add=2**FLAG_MOD_ADD @@ -1185,48 +1185,6 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema): return 0 -def update_machine_account_password(samdb, secrets_ldb, names): - """Update (change) the password of the current DC both in the SAM db and in - secret one - - :param samdb: An LDB object related to the sam.ldb file of a given provision - :param secrets_ldb: An LDB object related to the secrets.ldb file of a given - provision - :param names: List of key provision parameters""" - - message(SIMPLE, "Update machine account") - expression = "samAccountName=%s$" % names.netbiosname - secrets_msg = secrets_ldb.search(expression=expression, - attrs=["secureChannelType"]) - if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC: - res = samdb.search(expression=expression, attrs=[]) - assert(len(res) == 1) - - msg = Message(res[0].dn) - machinepass = samba.generate_random_password(128, 255) - msg["userPassword"] = MessageElement(machinepass, FLAG_MOD_REPLACE, - "userPassword") - samdb.modify(msg) - - res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname), - attrs=["msDs-keyVersionNumber"]) - assert(len(res) == 1) - kvno = int(str(res[0]["msDs-keyVersionNumber"])) - secChanType = int(secrets_msg[0]["secureChannelType"][0]) - - secretsdb_self_join(secrets_ldb, domain=names.domain, - realm=names.realm or sambaopts._lp.get('realm'), - domainsid=names.domainsid, - dnsdomain=names.dnsdomain, - netbiosname=names.netbiosname, - machinepass=machinepass, - key_version_number=kvno, - secure_channel_type=secChanType) - else: - raise ProvisioningError("Unable to find a Secure Channel" - "of type SEC_CHAN_BDC") - - def setup_path(file): return os.path.join(setup_dir, file) @@ -1455,14 +1413,14 @@ if __name__ == '__main__': # 12) schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn), - serverdn=str(names.serverdn)) + serverdn=str(names.serverdn)) # 13) if opts.full: if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, schema): - message(SIMPLE, "Rollbacking every changes. Check the reason" + message(SIMPLE, "Rollbacking every changes. Check the reason" " of the problem") - message(SIMPLE, "In any case your system as it was before" + message(SIMPLE, "In any case your system as it was before" " the upgrade") ldbs.groupedRollback() new_ldbs.groupedRollback() @@ -1471,6 +1429,7 @@ if __name__ == '__main__': # 14) update_secrets(new_ldbs.secrets, ldbs.secrets, message) # 15) + message(SIMPLE, "Update machine account") update_machine_account_password(ldbs.sam, ldbs.secrets, names) # 16) SD should be created with admin but as some previous acl were so wrong diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 74a157d041..9dbefba625 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -35,8 +35,9 @@ import ldb from samba.provision import (ProvisionNames, provision_paths_from_lp, getpolicypath, set_gpo_acl, create_gpo_struct, FILL_FULL, provision, ProvisioningError, - setsysvolacl) + setsysvolacl, secretsdb_self_join) from samba.dcerpc import misc, security, xattr +from samba.dcerpc.misc import SEC_CHAN_BDC from samba.ndr import ndr_unpack from samba.samdb import SamDB @@ -770,6 +771,48 @@ def construct_existor_expr(attrs): expr = "%s)"%expr return expr +def update_machine_account_password(samdb, secrets_ldb, names): + """Update (change) the password of the current DC both in the SAM db and in + secret one + + :param samdb: An LDB object related to the sam.ldb file of a given provision + :param secrets_ldb: An LDB object related to the secrets.ldb file of a given + provision + :param names: List of key provision parameters""" + + expression = "samAccountName=%s$" % names.netbiosname + secrets_msg = secrets_ldb.search(expression=expression, + attrs=["secureChannelType"]) + if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC: + res = samdb.search(expression=expression, attrs=[]) + assert(len(res) == 1) + + msg = ldb.Message(res[0].dn) + machinepass = samba.generate_random_password(128, 255) + msg["userPassword"] = ldb.MessageElement(machinepass, + ldb.FLAG_MOD_REPLACE, + "userPassword") + samdb.modify(msg) + + res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname), + attrs=["msDs-keyVersionNumber"]) + assert(len(res) == 1) + kvno = int(str(res[0]["msDs-keyVersionNumber"])) + secChanType = int(secrets_msg[0]["secureChannelType"][0]) + + secretsdb_self_join(secrets_ldb, domain=names.domain, + realm=names.realm, + domainsid=names.domainsid, + dnsdomain=names.dnsdomain, + netbiosname=names.netbiosname, + machinepass=machinepass, + key_version_number=kvno, + secure_channel_type=secChanType) + else: + raise ProvisioningError("Unable to find a Secure Channel" + "of type SEC_CHAN_BDC") + + def search_constructed_attrs_stored(samdb, rootdn, attrs): """Search a given sam DB for calculated attributes that are still stored in the db. -- cgit From 7ea70f86ac3a34d84082fed8b5f80ec1b46893bf Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 28 Jun 2010 13:49:08 +0400 Subject: s4: Add a simple script to change dc password This script will mostly be used by unit test (blackbox type) to test the change of the dc password Signed-off-by: Andrew Bartlett --- source4/scripting/devel/chgtdcpass | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100755 source4/scripting/devel/chgtdcpass (limited to 'source4') diff --git a/source4/scripting/devel/chgtdcpass b/source4/scripting/devel/chgtdcpass new file mode 100755 index 0000000000..1030531363 --- /dev/null +++ b/source4/scripting/devel/chgtdcpass @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# Copyright (C) Matthieu Patou 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 . + + +import optparse +import sys +# Allow to run from s4 source directory (without installing samba) +sys.path.insert(0, "bin/python") + +import samba.getopt as options +from samba.credentials import DONT_USE_KERBEROS +from samba.auth import system_session +from samba import param +from samba.upgradehelpers import (get_paths, + find_provision_key_parameters, get_ldbs, + update_machine_account_password) + +__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) + +opts = parser.parse_args()[0] + + +lp = sambaopts.get_loadparm() +smbconf = lp.configfile +creds = credopts.get_credentials(lp) +creds.set_kerberos_state(DONT_USE_KERBEROS) + + +if __name__ == '__main__': + paths = get_paths(param, smbconf=smbconf) + session = system_session() + + ldbs = get_ldbs(paths, creds, session, lp) + ldbs.startTransactions() + + names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, + paths, smbconf, lp) + + update_machine_account_password(ldbs.sam, ldbs.secrets, names) + ldbs.groupedCommit() -- cgit From 0496af8341b08ad2b8ceb42892ddde06af279c52 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 4 Jul 2010 16:39:17 +0400 Subject: s4: Unit test update_machine_account_password through kinit This patch is for testing the chgdcpass script which is mostly a call to update_machine_account_password. Signed-off-by: Andrew Bartlett --- source4/selftest/tests.sh | 1 + 1 file changed, 1 insertion(+) (limited to 'source4') diff --git a/source4/selftest/tests.sh b/source4/selftest/tests.sh index c70eb877cf..20e0a6af29 100755 --- a/source4/selftest/tests.sh +++ b/source4/selftest/tests.sh @@ -378,6 +378,7 @@ plantestsuite "blackbox.masktest" dc $samba4srcdir/torture/tests/test_masktest.s plantestsuite "blackbox.gentest" dc $samba4srcdir/torture/tests/test_gentest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX" plantestsuite "blackbox.wbinfo" dc:local $samba4srcdir/../nsswitch/tests/test_wbinfo.sh "\$DOMAIN" "\$USERNAME" "\$PASSWORD" "dc" plantestsuite "blackbox.wbinfo" member:local $samba4srcdir/../nsswitch/tests/test_wbinfo.sh "\$DOMAIN" "\$DC_USERNAME" "\$DC_PASSWORD" "member" +plantestsuite "blackbox.chgdcpass" dc $bbdir/test_chgdcpass.sh "\$SERVER" "LOCALDC\\\$" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $SELFTEST_PREFIX/dc # Tests using the "Simple" NTVFS backend -- cgit From b67a71f00bb84f14f7568d0afe143843d31a0a45 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 21 Jun 2010 10:55:18 +0400 Subject: ldb: allow ldb_sequence_number to be called in python Signed-off-by: Andrew Bartlett --- source4/lib/ldb/pyldb.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'source4') diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c index b886a4b81c..f1a02c90b1 100644 --- a/source4/lib/ldb/pyldb.c +++ b/source4/lib/ldb/pyldb.c @@ -1249,6 +1249,26 @@ static PyObject *py_ldb_modules(PyLdbObject *self) return ret; } +static PyObject *py_ldb_sequence_number(PyLdbObject *self, PyObject *args) +{ + struct ldb_context *ldb = PyLdb_AsLdbContext(self); + int type, ret; + uint64_t value; + + /* type "int" rather than "enum" for "scope" is intentional */ + if (!PyArg_ParseTuple(args, "i", &type)) + return NULL; + + /* FIXME: More interpretation */ + + ret = ldb_sequence_number(ldb, type, &value); + + if (ret != LDB_SUCCESS) { + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, ldb); + return NULL; + } + return PyLong_FromLongLong(value); +} static PyMethodDef py_ldb_methods[] = { { "set_debug", (PyCFunction)py_ldb_set_debug, METH_VARARGS, "S.set_debug(callback) -> None\n" @@ -1335,6 +1355,9 @@ static PyMethodDef py_ldb_methods[] = { { "modules", (PyCFunction)py_ldb_modules, METH_NOARGS, "S.modules() -> list\n" "Return the list of modules on this LDB connection " }, + { "sequence_number", (PyCFunction)py_ldb_sequence_number, METH_VARARGS, + "S.sequence_number(type) -> value\n" + "Return the value of the sequence according to the requested type" }, { NULL }, }; @@ -2594,6 +2617,9 @@ void initldb(void) if (m == NULL) return; + PyModule_AddObject(m, "SEQ_HIGHEST_SEQ", PyInt_FromLong(LDB_SEQ_HIGHEST_SEQ)); + PyModule_AddObject(m, "SEQ_HIGHEST_TIMESTAMP", PyInt_FromLong(LDB_SEQ_HIGHEST_TIMESTAMP)); + PyModule_AddObject(m, "SEQ_NEXT", PyInt_FromLong(LDB_SEQ_NEXT)); PyModule_AddObject(m, "SCOPE_DEFAULT", PyInt_FromLong(LDB_SCOPE_DEFAULT)); PyModule_AddObject(m, "SCOPE_BASE", PyInt_FromLong(LDB_SCOPE_BASE)); PyModule_AddObject(m, "SCOPE_ONELEVEL", PyInt_FromLong(LDB_SCOPE_ONELEVEL)); -- cgit From d861ebbd8167cf6da6d7565799dbc5267adf6bae Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 22 Jun 2010 19:56:37 +0400 Subject: s4 dsdb: create a new control: changereplmetadata This control is designed to allow replmetadata to be specified Signed-off-by: Andrew Bartlett --- source4/dsdb/samdb/samdb.h | 6 ++++++ source4/setup/schema_samba4.ldif | 1 + 2 files changed, 7 insertions(+) (limited to 'source4') diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 8b45cd0180..75aae7f108 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -92,6 +92,12 @@ struct dsdb_control_password_change_status { */ #define DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID "1.3.6.1.4.1.7165.4.3.12" +/** + OID used to allow the replacement of replPropertyMetaData. + It is used when the current replmetadata needs to be edited. +*/ +#define DSDB_CONTROL_CHANGEREPLMETADATA_OID "1.3.6.1.4.1.7165.4.3.14" + #define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1" struct dsdb_extended_replicated_object { struct ldb_message *msg; diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif index 681aa96898..aecd273a18 100644 --- a/source4/setup/schema_samba4.ldif +++ b/source4/setup/schema_samba4.ldif @@ -186,6 +186,7 @@ #Allocated: DSDB_CONTROL_APPLY_LINKS 1.3.6.1.4.1.7165.4.3.11 #Allocated: DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID 1.3.6.1.4.1.7165.4.3.12 #Allocated: LDB_CONTROL_BYPASSOPERATIONAL_OID 1.3.6.1.4.1.7165.4.3.13 +#Allocated: DSDB_CONTROL_CHANGEREPLMETADATA_OID 1.3.6.1.4.1.7165.4.3.14 # Extended 1.3.6.1.4.1.7165.4.4.x #Allocated: DSDB_EXTENDED_REPLICATED_OBJECTS_OID 1.3.6.1.4.1.7165.4.4.1 -- cgit From 6a0856da9cc075acaa7fcb6bad614f8f403df9c7 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Wed, 16 Jun 2010 18:47:18 +0400 Subject: s4 dsdb: Use the changereplmetadata control This control allow to specify the replPropertyMetaData attribute to be specified on modify request. It can be used for very specific needs to tweak the content of the replication data. Signed-off-by: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 205 +++++++++++++++++------- source4/scripting/python/samba/tests/dsdb.py | 89 ++++++++-- 2 files changed, 222 insertions(+), 72 deletions(-) (limited to 'source4') diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 90af17f7ec..2205be07c1 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -5,6 +5,7 @@ Copyright (C) Andrew Bartlett 2005 Copyright (C) Andrew Tridgell 2005 Copyright (C) Stefan Metzmacher 2007 + Copyright (C) Matthieu Patou 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 @@ -1073,6 +1074,20 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, return LDB_SUCCESS; } +static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd) +{ + uint32_t count = omd.ctr.ctr1.count; + uint64_t max = 0; + uint32_t i; + for (i=0; i < count; i++) { + struct replPropertyMetaData1 m = omd.ctr.ctr1.array[i]; + if (max < m.local_usn) { + max = m.local_usn; + } + } + return max; +} + /* * update the replPropertyMetaData object each time we modify an * object. This is needed for DRS replication, as the merge on the @@ -1093,11 +1108,12 @@ static int replmd_update_rpmd(struct ldb_module *module, const struct GUID *our_invocation_id; int ret; const char *attrs[] = { "replPropertyMetaData", "*", NULL }; + const char *attrs2[] = { "uSNChanged", "objectClass", NULL }; struct ldb_result *res; struct ldb_context *ldb; struct ldb_message_element *objectclass_el; enum urgent_situation situation; - bool rodc; + bool rodc, rmd_is_provided; ldb = ldb_module_get_ctx(module); @@ -1111,21 +1127,10 @@ static int replmd_update_rpmd(struct ldb_module *module, unix_to_nt_time(&now, t); - /* search for the existing replPropertyMetaDataBlob. We need - * to use REVEAL and ask for DNs in storage format to support - * the check for values being the same in - * replmd_update_rpmd_element() - */ - ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs, - DSDB_FLAG_NEXT_MODULE | - DSDB_SEARCH_SHOW_DELETED | - DSDB_SEARCH_SHOW_EXTENDED_DN | - DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | - DSDB_SEARCH_REVEAL_INTERNALS); - if (ret != LDB_SUCCESS || res->count != 1) { - DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n", - ldb_dn_get_linearized(msg->dn))); - return LDB_ERR_OPERATIONS_ERROR; + if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) { + rmd_is_provided = true; + } else { + rmd_is_provided = false; } /* if isDeleted is present and is TRUE, then we consider we are deleting, @@ -1136,62 +1141,140 @@ static int replmd_update_rpmd(struct ldb_module *module, situation = REPL_URGENT_ON_UPDATE; } - objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass"); - if (is_urgent && replmd_check_urgent_objectclass(objectclass_el, - situation)) { - *is_urgent = true; - } + if (rmd_is_provided) { + /* In this case the change_replmetadata control was supplied */ + /* We check that it's the only attribute that is provided + * (it's a rare case so it's better to keep the code simplier) + * We also check that the highest local_usn is bigger than + * uSNChanged. */ + uint64_t db_seq; + if( msg->num_elements != 1 || + strncmp(msg->elements[0].name, + "replPropertyMetaData", 20) ) { + DEBUG(0,(__location__ ": changereplmetada control called without "\ + "a specified replPropertyMetaData attribute or with others\n")); + return LDB_ERR_OPERATIONS_ERROR; + } + if (situation == REPL_URGENT_ON_DELETE) { + DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n")); + return LDB_ERR_OPERATIONS_ERROR; + } + omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData"); + if (!omd_value) { + DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd, + (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + *seq_num = find_max_local_usn(omd); - omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData"); - if (!omd_value) { - DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n", - ldb_dn_get_linearized(msg->dn))); - return LDB_ERR_OPERATIONS_ERROR; - } + ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_SHOW_EXTENDED_DN | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | + DSDB_SEARCH_REVEAL_INTERNALS); - ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd, - (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n", - ldb_dn_get_linearized(msg->dn))); - return LDB_ERR_OPERATIONS_ERROR; - } + if (ret != LDB_SUCCESS || res->count != 1) { + DEBUG(0,(__location__ ": Object %s failed to find uSNChanged\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } - if (omd.version != 1) { - DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n", - omd.version, ldb_dn_get_linearized(msg->dn))); - return LDB_ERR_OPERATIONS_ERROR; - } + objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass"); + if (is_urgent && replmd_check_urgent_objectclass(objectclass_el, + situation)) { + *is_urgent = true; + } - /*we have elements that will be modified*/ - if (msg->num_elements > 0) { - /*if we are RODC and this is a DRSR update then its ok*/ - if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) { - ret = samdb_rodc(ldb, &rodc); - if (ret != LDB_SUCCESS) { - DEBUG(4, (__location__ ": unable to tell if we are an RODC\n")); - } else if (rodc) { - ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n"); - return LDB_ERR_REFERRAL; - } + db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0); + if (*seq_num <= db_seq) { + DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\ + " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n", + *seq_num, db_seq)); + return LDB_ERR_OPERATIONS_ERROR; } - } - for (i=0; inum_elements; i++) { - struct ldb_message_element *old_el; - old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name); - ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num, - our_invocation_id, now); - if (ret != LDB_SUCCESS) { - return ret; + } else { + /* search for the existing replPropertyMetaDataBlob. We need + * to use REVEAL and ask for DNs in storage format to support + * the check for values being the same in + * replmd_update_rpmd_element() + */ + ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_SHOW_EXTENDED_DN | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | + DSDB_SEARCH_REVEAL_INTERNALS); + if (ret != LDB_SUCCESS || res->count != 1) { + DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; } - if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) { - *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]); + objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass"); + if (is_urgent && replmd_check_urgent_objectclass(objectclass_el, + situation)) { + *is_urgent = true; } - } + omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData"); + if (!omd_value) { + DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd, + (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (omd.version != 1) { + DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n", + omd.version, ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + /*we have elements that will be modified*/ + if (msg->num_elements > 0) { + /*if we are RODC and this is a DRSR update then its ok*/ + if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) { + ret = samdb_rodc(ldb, &rodc); + if (ret != LDB_SUCCESS) { + DEBUG(4, (__location__ ": unable to tell if we are an RODC\n")); + } else if (rodc) { + ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n"); + return LDB_ERR_REFERRAL; + } + } + } + + for (i=0; inum_elements; i++) { + struct ldb_message_element *old_el; + old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name); + ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num, + our_invocation_id, now); + if (ret != LDB_SUCCESS) { + return ret; + } + + if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) { + *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]); + } + + } + } /* * replmd_update_rpmd_element has done an update if the * seq_num is set diff --git a/source4/scripting/python/samba/tests/dsdb.py b/source4/scripting/python/samba/tests/dsdb.py index c19dbaab47..d28be82984 100644 --- a/source4/scripting/python/samba/tests/dsdb.py +++ b/source4/scripting/python/samba/tests/dsdb.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Unix SMB/CIFS implementation. Tests for dsdb +# Unix SMB/CIFS implementation. Tests for dsdb # Copyright (C) Matthieu Patou 2010 # # This program is free software; you can redistribute it and/or modify @@ -17,26 +17,93 @@ # along with this program. If not, see . # -import samba.dsdb from samba.credentials import Credentials from samba.samdb import SamDB from samba.auth import system_session from testtools.testcase import TestCase +from samba.ndr import ndr_unpack, ndr_pack +from samba.dcerpc import drsblobs +import ldb import os +import samba.dsdb class DsdbTests(TestCase): - def _baseprovpath(self): + + def setUp(self): + super(DsdbTests, self).setUp() + self.lp = samba.param.LoadParm() + self.lp.load(os.path.join(os.path.join(self.baseprovpath(), "etc"), "smb.conf")) + self.creds = Credentials() + self.creds.guess(self.lp) + self.session = system_session() + self.samdb = SamDB(os.path.join(self.baseprovpath(), "private", "sam.ldb"), + session_info=self.session, credentials=self.creds,lp=self.lp) + + + def baseprovpath(self): return os.path.join(os.environ['SELFTEST_PREFIX'], "dc") + def test_get_oid_from_attrid(self): - lp = samba.param.LoadParm() - lp.load(os.path.join(os.path.join(self._baseprovpath(), "etc"), "smb.conf")) - creds = Credentials() - creds.guess(lp) - session = system_session() - test_ldb = SamDB(os.path.join(self._baseprovpath(), "private", "sam.ldb"), - session_info=session, credentials=creds,lp=lp) - oid = test_ldb.get_oid_from_attid(591614) + oid = self.samdb.get_oid_from_attid(591614) self.assertEquals(oid, "1.2.840.113556.1.4.1790") + + def test_error_replpropertymetadata(self): + res = self.samdb.search(expression="cn=Administrator", + scope=ldb.SCOPE_SUBTREE, + attrs=["replPropertyMetaData"]) + repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, + str(res[0]["replPropertyMetaData"])) + ctr = repl.ctr + for o in ctr.array: + # Search for Description + if o.attid == 13: + old_version = o.version + o.version = o.version + 1 + replBlob = ndr_pack(repl) + msg = ldb.Message() + msg.dn = res[0].dn + msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") + self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) + + def test_twoatt_replpropertymetadata(self): + res = self.samdb.search(expression="cn=Administrator", + scope=ldb.SCOPE_SUBTREE, + attrs=["replPropertyMetaData", "uSNChanged"]) + repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, + str(res[0]["replPropertyMetaData"])) + ctr = repl.ctr + for o in ctr.array: + # Search for Description + if o.attid == 13: + old_version = o.version + o.version = o.version + 1 + o.local_usn = long(str(res[0]["uSNChanged"])) + 1 + replBlob = ndr_pack(repl) + msg = ldb.Message() + msg.dn = res[0].dn + msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") + msg["description"] = ldb.MessageElement("new val", ldb.FLAG_MOD_REPLACE, "description") + self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) + + def test_set_replpropertymetadata(self): + res = self.samdb.search(expression="cn=Administrator", + scope=ldb.SCOPE_SUBTREE, + attrs=["replPropertyMetaData", "uSNChanged"]) + repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, + str(res[0]["replPropertyMetaData"])) + ctr = repl.ctr + for o in ctr.array: + # Search for Description + if o.attid == 13: + old_version = o.version + o.version = o.version + 1 + o.local_usn = long(str(res[0]["uSNChanged"])) + 1 + o.originating_usn = long(str(res[0]["uSNChanged"])) + 1 + replBlob = ndr_pack(repl) + msg = ldb.Message() + msg.dn = res[0].dn + msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") + self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) -- cgit From f97c90c9cd124314b4a0862e702dd021bd2df9a0 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 22 Jun 2010 20:03:15 +0400 Subject: s4 python: Add functions to samdb to manipulate version of replPropertyMetaData attribute This change contains also helpers for attribute id to attribute oid conversion and from attribute id to attribute name. It brings also unit tests Signed-off-by: Andrew Bartlett --- source4/scripting/python/samba/samdb.py | 103 +++++++++++++++++++++++++++ source4/scripting/python/samba/tests/dsdb.py | 26 ++++++- 2 files changed, 128 insertions(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index f810926710..f358747b51 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -28,12 +28,16 @@ import samba import ldb import time import base64 +from samba.ndr import ndr_unpack, ndr_pack +from samba.dcerpc import drsblobs, misc __docformat__ = "restructuredText" class SamDB(samba.Ldb): """The SAM database.""" + hash_oid_name = {} + def __init__(self, url=None, lp=None, modules_dir=None, session_info=None, credentials=None, flags=0, options=None, global_schema=True, auto_connect=True, am_rodc=False): @@ -470,5 +474,104 @@ accountExpires: %u def set_schema_from_ldb(self, ldb): dsdb._dsdb_set_schema_from_ldb(self, ldb) + def get_attribute_from_attid(self, attid): + """ Get from an attid the associated attribute + + :param attid: The attribute id for searched attribute + :return: The name of the attribute associated with this id + """ + if len(self.hash_oid_name.keys()) == 0: + self._populate_oid_attid() + if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)): + return self.hash_oid_name[self.get_oid_from_attid(attid)] + else: + return None + + + def _populate_oid_attid(self): + """Populate the hash hash_oid_name + + This hash contains the oid of the attribute as a key and + its display name as a value + """ + self.hash_oid_name = {} + res = self.search(expression="objectClass=attributeSchema", + controls=["search_options:1:2"], + attrs=["attributeID", + "lDAPDisplayName"]) + if len(res) > 0: + for e in res: + strDisplay = str(e.get("lDAPDisplayName")) + self.hash_oid_name[str(e.get("attributeID"))] = strDisplay + + + def get_attribute_replmetadata_version(self, dn, att): + """ Get the version field trom the replPropertyMetaData for + the given field + + :param dn: The on which we want to get the version + :param att: The name of the attribute + :return: The value of the version field in the replPropertyMetaData + for the given attribute. None if the attribute is not replicated + """ + + res = self.search(expression="dn=%s" % dn, + scope=ldb.SCOPE_SUBTREE, + controls=["search_options:1:2"], + attrs=["replPropertyMetaData"]) + if len(res) == 0: + return None + + repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, + str(res[0]["replPropertyMetaData"])) + ctr = repl.ctr + if len(self.hash_oid_name.keys()) == 0: + self._populate_oid_attid() + for o in ctr.array: + # Search for Description + att_oid = self.get_oid_from_attid(o.attid) + if self.hash_oid_name.has_key(att_oid) and\ + att.lower() == self.hash_oid_name[att_oid].lower(): + return o.version + return None + + + def set_attribute_replmetadata_version(self, dn, att, value): + res = self.search(expression="dn=%s" % dn, + scope=ldb.SCOPE_SUBTREE, + controls=["search_options:1:2"], + attrs=["replPropertyMetaData"]) + if len(res) == 0: + return None + + repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, + str(res[0]["replPropertyMetaData"])) + ctr = repl.ctr + now = samba.unix2nttime(int(time.time())) + found = False + if len(self.hash_oid_name.keys()) == 0: + self._populate_oid_attid() + for o in ctr.array: + # Search for Description + att_oid = self.get_oid_from_attid(o.attid) + if self.hash_oid_name.has_key(att_oid) and\ + att.lower() == self.hash_oid_name[att_oid].lower(): + found = True + seq = self.sequence_number(ldb.SEQ_NEXT) + o.version = value + o.originating_change_time = now + o.originating_invocation_id = misc.GUID(self.get_invocation_id()) + o.originating_usn = seq + o.local_usn = seq + if found : + replBlob = ndr_pack(repl) + msg = ldb.Message() + msg.dn = res[0].dn + msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, + ldb.FLAG_MOD_REPLACE, + "replPropertyMetaData") + self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) + + def write_prefixes_from_schema(self): dsdb._dsdb_write_prefixes_from_schema_to_ldb(self) diff --git a/source4/scripting/python/samba/tests/dsdb.py b/source4/scripting/python/samba/tests/dsdb.py index d28be82984..4a50c96e25 100644 --- a/source4/scripting/python/samba/tests/dsdb.py +++ b/source4/scripting/python/samba/tests/dsdb.py @@ -25,7 +25,7 @@ from samba.ndr import ndr_unpack, ndr_pack from samba.dcerpc import drsblobs import ldb import os -import samba.dsdb +import samba class DsdbTests(TestCase): @@ -107,3 +107,27 @@ class DsdbTests(TestCase): msg.dn = res[0].dn msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) + + def test_ok_get_attribute_from_attid(self): + self.assertEquals(self.samdb.get_attribute_from_attid(13), "description") + + def test_ko_get_attribute_from_attid(self): + self.assertEquals(self.samdb.get_attribute_from_attid(11979), None) + + def test_get_attribute_replmetadata_version(self): + res = self.samdb.search(expression="cn=Administrator", + scope=ldb.SCOPE_SUBTREE, + attrs=["dn"]) + self.assertEquals(len(res), 1) + dn = str(res[0].dn) + self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "unicodePwd"), 1) + + def test_set_attribute_replmetadata_version(self): + res = self.samdb.search(expression="cn=Administrator", + scope=ldb.SCOPE_SUBTREE, + attrs=["dn"]) + self.assertEquals(len(res), 1) + dn = str(res[0].dn) + version = self.samdb.get_attribute_replmetadata_version(dn, "description") + self.samdb.set_attribute_replmetadata_version(dn, "description", version + 2) + self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "description"), version + 2) -- cgit From 93239016443c1ba40ec69c025a91d60c09833c20 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 15 Jun 2010 12:49:19 +0400 Subject: s4 upgradeprovision: introduce a new function to update the field use for calculating msds-keyversionnumber This function change the version field of the unicodePwd in the replPropertyMetaData so that the version is equal or superior to the reference value passed. Signed-off-by: Andrew Bartlett --- source4/scripting/python/samba/upgradehelpers.py | 40 ++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) (limited to 'source4') diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 9dbefba625..58106e0a70 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -704,14 +704,48 @@ def update_gpo(paths, samdb, names, lp, message, force=0): set_gpo_acl(paths.sysvol, names.dnsdomain, names.domainsid, names.domaindn, samdb, lp) except TypeError, e: - message(ERROR, "Unable to set ACLs on policies related objects, if not using posix:eadb, you must be root to do it") + message(ERROR, "Unable to set ACLs on policies related objects," + " if not using posix:eadb, you must be root to do it") if resetacls: try: setsysvolacl(samdb, paths.netlogon, paths.sysvol, names.wheel_gid, names.domainsid, names.dnsdomain, names.domaindn, lp) except TypeError, e: - message(ERROR, "Unable to set ACLs on sysvol share, if not using posix:eadb, you must be root to do it") + message(ERROR, "Unable to set ACLs on sysvol share, if not using" + "posix:eadb, you must be root to do it") + +def increment_calculated_keyversion_number(samdb, rootdn, hashDns): + """For a given hash associating dn and a number, this function will + update the replPropertyMetaData of each dn in the hash, so that the + calculated value of the msDs-KeyVersionNumber is equal or superior to the + one associated to the given dn. + + :param samdb: An SamDB object pointing to the sam + :param rootdn: The base DN where we want to start + :param hashDns: A hash with dn as key and number representing the + minimum value of msDs-KeyVersionNumber that we want to + have + """ + entry = samdb.search(expression='(objectClass=user)', + base=ldb.Dn(samdb,str(rootdn)), + scope=SCOPE_SUBTREE, attrs=["msDs-KeyVersionNumber"], + controls=["search_options:1:2"]) + done = 0 + if len(entry) == 0: + raise ProvisioningError("Unable to find msDs-KeyVersionNumber") + else: + for e in entry: + if hashDns.has_key(str(e.dn).lower()): + done = done + 1 + val = e.get("msDs-KeyVersionNumber") + if not val: + continue + version = int(str(hashDns[str(e.dn).lower()])) + if int(str(val)) < version: + samdb.set_attribute_replmetadata_version(str(e.dn), + "unicodePwd", + version) def delta_update_basesamdb(refsam, sam, creds, session, lp, message): """Update the provision container db: sam.ldb @@ -829,7 +863,7 @@ def search_constructed_attrs_stored(samdb, rootdn, attrs): expr = construct_existor_expr(attrs) if expr == "": return hashAtt - entry = samdb.search(expression=expr, base=ldb.Dn(samdb,str(rootdn)), + entry = samdb.search(expression=expr, base=ldb.Dn(samdb, str(rootdn)), scope=SCOPE_SUBTREE, attrs=attrs, controls=["search_options:1:2","bypassoperational:0"]) if len(entry) == 0: -- cgit From 62a32975c86e71eb1c5efeec0a4dee9d1ac20c10 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Tue, 15 Jun 2010 12:54:05 +0400 Subject: s4: Add unit test for increment_calculated_keyversion_number Signed-off-by: Andrew Bartlett --- .../python/samba/tests/upgradeprovisionneeddc.py | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py index b32ffc6155..e30906fc6b 100644 --- a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py +++ b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py @@ -29,7 +29,8 @@ from samba.upgradehelpers import (get_paths, get_ldbs, find_provision_key_parameters, identic_rename, updateOEMInfo, getOEMInfo, update_gpo, delta_update_basesamdb, - search_constructed_attrs_stored) + search_constructed_attrs_stored, + increment_calculated_keyversion_number) from samba.tests import env_loadparm, TestCaseInTempDir from samba.tests.provision import create_dummy_secretsdb import ldb @@ -91,6 +92,29 @@ class UpgradeProvisionWithLdbTestCase(TestCaseInTempDir): ["msds-KeyVersionNumber"]) self.assertFalse(hashAtt.has_key("msds-KeyVersionNumber")) + def test_increment_calculated_keyversion_number(self): + dn = "CN=Administrator,CN=Users,%s" % self.names.rootdn + # We conctruct a simple hash for the user administrator + hash = {} + # And we want the version to be 140 + hash[dn.lower()] = 140 + + increment_calculated_keyversion_number(self.ldbs.sam, + self.names.rootdn, + hash) + self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn, + "unicodePwd"), + 140) + # This function should not decrement the version + hash[dn.lower()] = 130 + + increment_calculated_keyversion_number(self.ldbs.sam, + self.names.rootdn, + hash) + self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn, + "unicodePwd"), + 140) + def test_identic_rename(self): rootdn = "DC=samba,DC=example,DC=com" -- cgit From 6c51b3a43298e43332893f52d6951bf475bae6af Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 3 Jul 2010 16:53:44 +0400 Subject: s4 upgradeprovision: fix whitespaces Signed-off-by: Andrew Bartlett --- source4/scripting/bin/upgradeprovision | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 0a22a3c747..400d629602 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -320,7 +320,7 @@ def handle_special_case(att, delta, new, old, usn): delta.remove(att) return True - if (att in ("gPLink", "gPCFileSysPath") and + if (att in ("gPLink", "gPCFileSysPath") and flag == FLAG_MOD_REPLACE and str(new[0].dn).lower() == str(old[0].dn).lower()): delta.remove(att) @@ -530,8 +530,8 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index): depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index, delta.get(str(att))) if depend_on_yet_tobecreated is not None: - message(CHANGE, "Object %s depends on %s in attribute %s," - "delaying the creation" % (dn, + message(CHANGE, "Object %s depends on %s in attribute %s," + "delaying the creation" % (dn, depend_on_yet_tobecreated, att)) return False @@ -827,16 +827,16 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): if attrUSN == -1: # This attribute was last modified by another DC forget # about it - message(CHANGE, "%sAttribute: %s has been" + message(CHANGE, "%sAttribute: %s has been" "created/modified/deleted by another DC," " do nothing" % (txt, att )) txt = "" delta.remove(att) continue elif not usn_in_range(int(attrUSN), usns): - message(CHANGE, "%sAttribute: %s has been" - "created/modified/deleted not during a" - " provision or upgradeprovision: current" + message(CHANGE, "%sAttribute: %s has been" + "created/modified/deleted not during a" + " provision or upgradeprovision: current" " usn %d , do nothing" % (txt, att, attrUSN)) txt = "" delta.remove(att) @@ -845,13 +845,13 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): if att == "defaultSecurityDescriptor": defSDmodified = True if attrUSN: - message(CHANGE, "%sAttribute: %s will be modified" - "/deleted it was last modified" - "during a provision, current usn:" + 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" + message(CHANGE, "%sAttribute: %s will be added because" " it hasn't existed before " % (txt, att)) txt = "" continue @@ -893,7 +893,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): delta.dn = dn if len(delta.items()) >1: attributes=", ".join(delta.keys()) - message(CHANGE, "%s is different from the reference one, changed" + message(CHANGE, "%s is different from the reference one, changed" " attributes: %s\n" % (dn, attributes)) changed += 1 samdb.modify(delta) @@ -1073,7 +1073,7 @@ def rebuild_sd(samdb, names): 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.configdn) or str(obj["dn"]) == str(names.schemadn)): hash[str(obj["dn"])] = obj["whenCreated"] -- cgit From 2afc2f20b65b28140274828249160f1483090b5e Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 3 Jul 2010 16:26:24 +0400 Subject: s4 upgradeprovision: add function to backup the provision before updating Signed-off-by: Andrew Bartlett --- source4/scripting/bin/upgradeprovision | 420 +++++++++++++++++++++------------ 1 file changed, 268 insertions(+), 152 deletions(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 400d629602..c22f3ce43b 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -47,7 +47,7 @@ from samba.provision import (find_setup_dir, get_domain_descriptor, ProvisioningError, get_last_provision_usn, get_max_usn, update_provision_usn) from samba.schema import get_linked_attributes, Schema, get_schema_descriptor -from samba.dcerpc import security, drsblobs +from samba.dcerpc import security, drsblobs, xattr from samba.ndr import ndr_unpack from samba.upgradehelpers import (dn_sort, get_paths, newprovision, find_provision_key_parameters, get_ldbs, @@ -1002,6 +1002,7 @@ def check_updated_sd(ref_sam, cur_sam, names): str(reference[i]["nTSecurityDescriptor"])) hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid) + for i in range(0, len(current)): key = str(current[i]["dn"]).lower() if hash.has_key(key): @@ -1113,6 +1114,18 @@ def removeProvisionUSN(samdb): delta.dn = entry[0].dn samdb.modify(delta) +def remove_stored_generated_attrs(paths, creds, session, lp): + """Remove previously stored constructed attributes + + :param paths: List of paths for different provision objects + from the upgraded provision + :param creds: A credential object + :param session: A session object + :param lp: A line parser object + :return: An associative array whose key are the different constructed + attributes and the value the dn where this attributes were found. + """ + def simple_update_basesamdb(newpaths, paths, names): """Update the provision container db: sam.ldb @@ -1185,6 +1198,96 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema): return 0 +def copyxattrs(dir, refdir): + """ Copy owner, groups, extended ACL and NT acls from + a reference dir to a destination dir + + Both dir are supposed to hold the same files + :param dir: Destination dir + :param refdir: Reference directory""" + + noxattr = 0 + for root, dirs, files in os.walk(dir, topdown=True): + for name in files: + subdir=root[len(dir):] + ref = os.path.join("%s%s" % (refdir, subdir), name) + statsinfo = os.stat(ref) + tgt = os.path.join(root, name) + try: + + os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid) + # Get the xattr attributes if any + try: + attribute = samba.xattr_native.wrap_getxattr(ref, + xattr.XATTR_NTACL_NAME) + samba.xattr_native.wrap_setxattr(tgt, + xattr.XATTR_NTACL_NAME, + attribute) + except: + noxattr = 1 + attribute = samba.xattr_native.wrap_getxattr(ref, + "system.posix_acl_access") + samba.xattr_native.wrap_setxattr(tgt, + "system.posix_acl_access", + attribute) + except: + continue + for name in dirs: + subdir=root[len(dir):] + ref = os.path.join("%s%s" % (refdir, subdir), name) + statsinfo = os.stat(ref) + tgt = os.path.join(root, name) + try: + os.chown(os.path.join(root, name), statsinfo.st_uid, + statsinfo.st_gid) + try: + attribute = samba.xattr_native.wrap_getxattr(ref, + xattr.XATTR_NTACL_NAME) + samba.xattr_native.wrap_setxattr(tgt, + xattr.XATTR_NTACL_NAME, + attribute) + except: + noxattr = 1 + attribute = samba.xattr_native.wrap_getxattr(ref, + "system.posix_acl_access") + samba.xattr_native.wrap_setxattr(tgt, + "system.posix_acl_access", + attribute) + + except: + continue + + +def backup_provision(paths, dir): + """This function backup the provision files so that a rollback + is possible + + :param paths: Paths to different objects + :param dir: Directory where to store the backup + """ + + shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol")) + copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol) + shutil.copy2(paths.samdb, dir) + shutil.copy2(paths.secrets, dir) + shutil.copy2(paths.idmapdb, dir) + shutil.copy2(paths.privilege, dir) + if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")): + shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir) + shutil.copy2(paths.smbconf, dir) + shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir) + + samldbdir = os.path.join(paths.private_dir, "sam.ldb.d") + if not os.path.isdir(samldbdir): + samldbdir = paths.private_dir + schemaldb = os.path.join(paths.private_dir, "schema.ldb") + configldb = os.path.join(paths.private_dir, "configuration.ldb") + usersldb = os.path.join(paths.private_dir, "users.ldb") + shutil.copy2(schemaldb, dir) + shutil.copy2(usersldb, dir) + shutil.copy2(configldb, dir) + else: + shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d")) def setup_path(file): return os.path.join(setup_dir, file) @@ -1210,12 +1313,13 @@ def setup_path(file): # A) When alpha9 or alphaxx is present # The base sam.ldb file is updated by looking at the difference between # referrence one and the current one. Everything is copied with the -# exception of lastProvisionUSN attributes. The highest used USN -# is fetched so that changed by upgradeprovision usn can be tracked +# exception of lastProvisionUSN attributes. # B) Other case (it reflect that that provision was done before alpha9) # The base sam.ldb of the reference provision is copied over # the current one, if necessary ldb related to partitions are moved # and renamed +# The highest used USN is fetched so that changed by upgradeprovision +# usn can be tracked # 12)A Schema object is created, it will be used to provide a complete # schema to current provision during update (as the schema of the # current provision might not be complete and so won't allow some @@ -1338,158 +1442,170 @@ if __name__ == '__main__': minUSN = 0 # 2) ldbs = get_ldbs(paths, creds, session, lp) - ldbs.startTransactions() - - # 3) Guess all the needed names (variables in fact) from the current - # provision. - names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, - paths, smbconf, lp) - # 4) - lastProvisionUSNs = get_last_provision_usn(ldbs.sam) - if lastProvisionUSNs is not None: - message(CHANGE, - "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs)) - - # Objects will be created with the admin session - # (not anymore system session) - adm_session = admin_session(lp, str(names.domainsid)) - # So we reget handle on objects - # ldbs = get_ldbs(paths, creds, adm_session, lp) - - if not sanitychecks(ldbs.sam, names): - message(SIMPLE, "Sanity checks for the upgrade fails, checks messages" - " and correct them before rerunning upgradeprovision") - sys.exit(1) - - # Let's see provision parameters - print_provision_key_parameters(names) - - # 5) With all this information let's create a fresh new provision used as - # reference - message(SIMPLE, "Creating a reference provision") - provisiondir = tempfile.mkdtemp(dir=paths.private_dir, - prefix="referenceprovision") - newprovision(names, setup_dir, creds, session, smbconf, provisiondir, - provision_logger) - - # TODO - # 6) and 7) - # We need to get a list of object which SD is directly computed from - # defaultSecurityDescriptor. - # This will allow us to know which object we can rebuild the SD in case - # of change of the parent's SD or of the defaultSD. - # Get file paths of this new provision - newpaths = get_paths(param, targetdir=provisiondir) - new_ldbs = get_ldbs(newpaths, creds, session, lp) - new_ldbs.startTransactions() - - # 8) Populate some associative array to ease the update process - # List of attribute which are link and backlink - populate_links(new_ldbs.sam, names.schemadn) - # List of attribute with ASN DN synthax) - populate_dnsyntax(new_ldbs.sam, names.schemadn) - # 9) - update_privilege(newpaths.private_dir, paths.private_dir) - # 10) - oem = getOEMInfo(ldbs.sam, str(names.rootdn)) - # Do some modification on sam.ldb - ldbs.groupedCommit() - # 11) - if re.match(".*alpha((9)|(\d\d+)).*", str(oem)): - # 11) A - # Starting from alpha9 we can consider that the structure is quite ok - # and that we should do only dela + backupdir = tempfile.mkdtemp(dir=paths.private_dir, + prefix="backupprovision") + backup_provision(paths, backupdir) + try: + ldbs.startTransactions() + + # 3) Guess all the needed names (variables in fact) from the current + # provision. + names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, + paths, smbconf, lp) + # 4) + lastProvisionUSNs = get_last_provision_usn(ldbs.sam) + if lastProvisionUSNs is not None: + message(CHANGE, + "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs)) + + # Objects will be created with the admin session + # (not anymore system session) + adm_session = admin_session(lp, str(names.domainsid)) + # So we reget handle on objects + # ldbs = get_ldbs(paths, creds, adm_session, lp) + + if not sanitychecks(ldbs.sam, names): + message(SIMPLE, "Sanity checks for the upgrade fails, checks messages" + " and correct them before rerunning upgradeprovision") + sys.exit(1) + + # Let's see provision parameters + print_provision_key_parameters(names) + + # 5) With all this information let's create a fresh new provision used as + # reference + message(SIMPLE, "Creating a reference provision") + provisiondir = tempfile.mkdtemp(dir=paths.private_dir, + prefix="referenceprovision") + newprovision(names, setup_dir, creds, session, smbconf, provisiondir, + provision_logger) + + # TODO + # 6) and 7) + # We need to get a list of object which SD is directly computed from + # defaultSecurityDescriptor. + # This will allow us to know which object we can rebuild the SD in case + # of change of the parent's SD or of the defaultSD. + # Get file paths of this new provision + newpaths = get_paths(param, targetdir=provisiondir) + new_ldbs = get_ldbs(newpaths, creds, session, lp) + new_ldbs.startTransactions() + + # 8) Populate some associative array to ease the update process + # List of attribute which are link and backlink + populate_links(new_ldbs.sam, names.schemadn) + # List of attribute with ASN DN synthax) + populate_dnsyntax(new_ldbs.sam, names.schemadn) + # 9) + update_privilege(newpaths.private_dir, paths.private_dir) + # 10) + oem = getOEMInfo(ldbs.sam, str(names.rootdn)) + # Do some modification on sam.ldb + ldbs.groupedCommit() new_ldbs.groupedCommit() - delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message) + + # 11) + if re.match(".*alpha((9)|(\d\d+)).*", str(oem)): + # 11) A + # Starting from alpha9 we can consider that the structure is quite ok + # and that we should do only dela + delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message) + else: + # 11) B + simple_update_basesamdb(newpaths, paths, names) + ldbs = get_ldbs(paths, creds, session, lp) + removeProvisionUSN(ldbs.sam) + ldbs.startTransactions() minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1 new_ldbs.startTransactions() - else: - # 11) B - simple_update_basesamdb(newpaths, paths, names) - ldbs = get_ldbs(paths, creds, session, lp) - removeProvisionUSN(ldbs.sam) - ldbs.startTransactions() - # 12) - schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn), - serverdn=str(names.serverdn)) - # 13) - if opts.full: - if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, - schema): - message(SIMPLE, "Rollbacking every changes. Check the reason" - " of the problem") - message(SIMPLE, "In any case your system as it was before" - " the upgrade") - ldbs.groupedRollback() - new_ldbs.groupedRollback() - shutil.rmtree(provisiondir) - sys.exit(1) - # 14) - update_secrets(new_ldbs.secrets, ldbs.secrets, message) - # 15) - message(SIMPLE, "Update machine account") - update_machine_account_password(ldbs.sam, ldbs.secrets, names) - - # 16) SD should be created with admin but as some previous acl were so wrong - # that admin can't modify them we have first to recreate them with the good - # form but with system account and then give the ownership to admin ... - if not re.match(r'.*alpha(9|\d\d+)', str(oem)): - message(SIMPLE, "Fixing old povision SD") - fix_partition_sd(ldbs.sam, names) - rebuild_sd(ldbs.sam, names) - - # We calculate the max USN before recalculating the SD because we might - # touch object that have been modified after a provision and we do not - # want that the next upgradeprovision thinks that it has a green light - # to modify them - - # 17) - maxUSN = get_max_usn(ldbs.sam, str(names.rootdn)) - - # 18) We rebuild SD only if defaultSecurityDescriptor is modified - # But in fact we should do it also if one object has its SD modified as - # child might need rebuild - if defSDmodified: - message(SIMPLE, "Updating SD") - ldbs.sam.set_session_info(adm_session) - # Alpha10 was a bit broken still - if re.match(r'.*alpha(\d|10)', str(oem)): + # 12) + schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn), + serverdn=str(names.serverdn)) + + # 13) + if opts.full: + if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, + schema): + message(SIMPLE, "Rollbacking every changes. Check the reason" + " of the problem") + message(SIMPLE, "In any case your system as it was before" + " the upgrade") + ldbs.groupedRollback() + new_ldbs.groupedRollback() + shutil.rmtree(provisiondir) + sys.exit(1) + # 14) + update_secrets(new_ldbs.secrets, ldbs.secrets, message) + # 15) + message(SIMPLE, "Update machine account") + update_machine_account_password(ldbs.sam, ldbs.secrets, names) + + # 16) SD should be created with admin but as some previous acl were so wrong + # that admin can't modify them we have first to recreate them with the good + # form but with system account and then give the ownership to admin ... + if not re.match(r'.*alpha(9|\d\d+)', str(oem)): + message(SIMPLE, "Fixing old povision SD") fix_partition_sd(ldbs.sam, names) - rebuild_sd(ldbs.sam, names) - - # 19) - # Now we are quite confident in the recalculate process of the SD, we make - # it optional. - # Also the check must be done in a clever way as for the moment we just - # compare SDDL - if opts.debugchangesd: - check_updated_sd(new_ldbs.sam, ldbs.sam, names) - - # 20) - updateOEMInfo(ldbs.sam, str(names.rootdn)) - # 21) - check_for_DNS(newpaths.private_dir, paths.private_dir) - # 22) - if lastProvisionUSNs is not None: - update_provision_usn(ldbs.sam, minUSN, maxUSN) - if opts.full and (names.policyid is None or names.policyid_dc is None): - update_policyids(names, ldbs.sam) - if opts.full or opts.resetfileacl: - try: - update_gpo(paths, ldbs.sam, names, lp, message, 1) - except ProvisioningError, e: - message(ERROR, "The policy for domain controller is missing," - " you should restart upgradeprovision with --full") - else: - try: - update_gpo(paths, ldbs.sam, names, lp, message, 0) - except ProvisioningError, e: - message(ERROR, "The policy for domain controller is missing," - " you should restart upgradeprovision with --full") - ldbs.groupedCommit() - new_ldbs.groupedCommit() - message(SIMPLE, "Upgrade finished !") - # remove reference provision now that everything is done ! - shutil.rmtree(provisiondir) + rebuild_sd(ldbs.sam, names) + + # We calculate the max USN before recalculating the SD because we might + # touch object that have been modified after a provision and we do not + # want that the next upgradeprovision thinks that it has a green light + # to modify them + + # 17) + maxUSN = get_max_usn(ldbs.sam, str(names.rootdn)) + + # 18) We rebuild SD only if defaultSecurityDescriptor is modified + # But in fact we should do it also if one object has its SD modified as + # child might need rebuild + if defSDmodified: + message(SIMPLE, "Updating SD") + ldbs.sam.set_session_info(adm_session) + # Alpha10 was a bit broken still + if re.match(r'.*alpha(\d|10)', str(oem)): + fix_partition_sd(ldbs.sam, names) + rebuild_sd(ldbs.sam, names) + + # 19) + # Now we are quite confident in the recalculate process of the SD, we make + # it optional. + # Also the check must be done in a clever way as for the moment we just + # compare SDDL + if opts.debugchangesd: + check_updated_sd(new_ldbs.sam, ldbs.sam, names) + + # 20) + updateOEMInfo(ldbs.sam, str(names.rootdn)) + # 21) + check_for_DNS(newpaths.private_dir, paths.private_dir) + # 22) + if lastProvisionUSNs is not None: + update_provision_usn(ldbs.sam, minUSN, maxUSN) + if opts.full and (names.policyid is None or names.policyid_dc is None): + update_policyids(names, ldbs.sam) + if opts.full or opts.resetfileacl: + try: + update_gpo(paths, ldbs.sam, names, lp, message, 1) + except ProvisioningError, e: + message(ERROR, "The policy for domain controller is missing," + " you should restart upgradeprovision with --full") + else: + try: + update_gpo(paths, ldbs.sam, names, lp, message, 0) + except ProvisioningError, e: + message(ERROR, "The policy for domain controller is missing," + " you should restart upgradeprovision with --full") + ldbs.groupedCommit() + new_ldbs.groupedCommit() + message(SIMPLE, "Upgrade finished !") + # remove reference provision now that everything is done ! + shutil.rmtree(provisiondir) + except StandardError, err: + message(ERROR,"A problem has occured when trying to upgrade your provision," + " a full backup is located at %s" % backupdir) + if opts.changeall: + (typ, val, tb) = sys.exc_info() + traceback.print_exception(typ, val, tb) -- cgit From 0a1b1121c48e26c5218e2ccc0c8a8cc724b96624 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 5 Jul 2010 23:46:46 +0400 Subject: s4 upgradeprovision: do not copy RID Set it's automaticaly created by the RID manager Signed-off-by: Andrew Bartlett --- source4/scripting/bin/upgradeprovision | 58 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 24 deletions(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index c22f3ce43b..888f3a8256 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -516,30 +516,40 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index): empty = Message() delta = samdb.msg_diff(empty, reference[0]) delta.dn - if delta.get("objectSid"): - sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"]))) - m = re.match(r".*-(\d+)$", sid) - if m and int(m.group(1))>999: - delta.remove("objectSid") - for att in hashAttrNotCopied.keys(): - delta.remove(att) - for att in backlinked: - delta.remove(att) - depend_on_yettobecreated = None - for att in dn_syntax_att: - depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index, - delta.get(str(att))) - if depend_on_yet_tobecreated is not None: - message(CHANGE, "Object %s depends on %s in attribute %s," - "delaying the creation" % (dn, - depend_on_yet_tobecreated, att)) - return False - - delta.dn = dn - message(CHANGE,"Object %s will be added" % dn) - samdb.add(delta, ["relax:0"]) - - return True + skip = False + try: + if str(reference[0].get("cn")) == "RID Set": + for klass in reference[0].get("objectClass"): + if str(klass).lower == "ridset": + skip = True + finally: + if delta.get("objectSid"): + sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"]))) + m = re.match(r".*-(\d+)$", sid) + if m and int(m.group(1))>999: + delta.remove("objectSid") + for att in hashAttrNotCopied.keys(): + delta.remove(att) + for att in backlinked: + delta.remove(att) + depend_on_yettobecreated = None + for att in dn_syntax_att: + depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index, + delta.get(str(att))) + if depend_on_yet_tobecreated is not None: + message(CHANGE, "Object %s depends on %s in attribute %s," + "delaying the creation" % (dn, + depend_on_yet_tobecreated, att)) + return False + + delta.dn = dn + if not skip: + message(CHANGE,"Object %s will be added" % dn) + samdb.add(delta, ["relax:0"]) + else: + message(CHANGE,"Object %s was skipped" % dn) + + return True def gen_dn_index_hash(listMissing): """Generate a hash associating the DN to its creation order -- cgit From 7478224189598f192065b91d57080f1dd1719a99 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 5 Jul 2010 01:00:13 +0400 Subject: s4 upgradeprovision: Synchronize the calculated keyversionnumber with the one previously stored Signed-off-by: Andrew Bartlett --- source4/scripting/bin/upgradeprovision | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 888f3a8256..f073dbcad7 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -56,7 +56,9 @@ from samba.upgradehelpers import (dn_sort, get_paths, newprovision, CHANGEALL, GUESS, CHANGESD, PROVISION, updateOEMInfo, getOEMInfo, update_gpo, delta_update_basesamdb, update_policyids, - update_machine_account_password) + update_machine_account_password, + search_constructed_attrs_stored, + increment_calculated_keyversion_number) replace=2**FLAG_MOD_REPLACE add=2**FLAG_MOD_ADD @@ -1299,6 +1301,23 @@ def backup_provision(paths, dir): else: shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d")) + + + +def sync_calculated_attributes(samdb, names): + """Synchronize attributes used for constructed ones, with the + old constructed that were stored in the database. + + This apply for instance to msds-keyversionnumber that was + stored and that is now constructed from replpropertymetadata. + + :param samdb: An LDB object attached to the currently upgraded samdb + :param names: Various key parameter about current provision. + """ + listAttrs = ["msDs-KeyVersionAttribute"] + hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs) + increment_calculated_keyversion_number(samdb, names.rootdn, hash) + def setup_path(file): return os.path.join(setup_dir, file) @@ -1546,6 +1565,8 @@ if __name__ == '__main__': new_ldbs.groupedRollback() shutil.rmtree(provisiondir) sys.exit(1) + else: + sync_calculated_attributes(ldbs.sam, names) # 14) update_secrets(new_ldbs.secrets, ldbs.secrets, message) # 15) -- cgit From a748402f61b4b3ea0df6666f4ec90f42fb45eaf8 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 10 Jul 2010 14:48:40 +0400 Subject: s4 ldb modules: relax some tests about attributes that should not be here For attributes that we know that are harmless and that used to be stored in the ldb we relax the tests on the existance in a given objectclass. Signed-off-by: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/objectclass_attrs.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'source4') diff --git a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c index 6cfecc0d72..b9436e3a04 100644 --- a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c +++ b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c @@ -153,6 +153,12 @@ static int attr_handler(struct oc_context *ac) return ldb_next_request(ac->module, child_req); } +/* + these are attributes which are left over from old ways of doing + things in ldb, and are harmless + */ +static const char *harmless_attrs[] = { "parentGUID", NULL }; + static int attr_handler2(struct oc_context *ac) { struct ldb_context *ldb; @@ -218,6 +224,9 @@ static int attr_handler2(struct oc_context *ac) } else { found = str_list_check(may_contain, attr->lDAPDisplayName); } + if (!found) { + found = str_list_check(harmless_attrs, attr->lDAPDisplayName); + } if (!found) { ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!", msg->elements[i].name, -- cgit From 36b5feceee2ea23d6da757999f238e28ab7de485 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 11 Jul 2010 17:27:13 +0400 Subject: s4 upgradeprovision: Adapt the list of attribute modified * isMemberOfPartialAttributeSet is now allowed to be deleted (on schema objects) * attributeDisplayNames is now allowed to be added and modified (used on display specifiers) * spnMapping is now allowed to be altered on Directory Service objects * minPwdAge is now modified if the previous value was 0 We issue a clear information about the userControl attribute for administrator to invite the user to modify himself the value. Signed-off-by: Andrew Bartlett --- source4/scripting/bin/upgradeprovision | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index f073dbcad7..deb50e36fb 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -105,7 +105,9 @@ hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace, "wellKnownObjects":replace, "privilege":never, "defaultSecurityDescriptor": replace, "rIDAvailablePool": never, - "defaultSecurityDescriptor": replace + add } + "defaultSecurityDescriptor": replace + add, + "isMemberOfPartialAttributeSet": delete, + "attributeDisplayNames": replace + add} backlinked = [] @@ -287,7 +289,7 @@ def print_provision_key_parameters(names): message(GUESS, "domainlevel :" + str(names.domainlevel)) -def handle_special_case(att, delta, new, old, usn): +def handle_special_case(att, delta, new, old, usn, basedn, aldb): """Define more complicate update rules for some attributes :param att: The attribute to be updated @@ -296,6 +298,8 @@ def handle_special_case(att, delta, new, old, usn): :param new: The reference object :param old: The Updated object :param usn: The highest usn modified by a previous (upgrade)provision + :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 discarding it""" @@ -304,6 +308,23 @@ def handle_special_case(att, delta, new, old, usn): # highest usn as otherwise the replPropertyMetaData will guide us more # correctly if usn is None: + if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and + ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT," + "CN=Services,CN=Configuration,%s" % basedn) + == old[0].dn): + return True + if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and + ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn) + == old[0].dn): + message(SIMPLE, "We suggest that you change the userAccountControl" + " for user Administrator from value %d to %d" % + (int(str(old[0][att])), int(str(new[0][att])))) + return False + if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE): + if (long(str(old[0][att])) == 0): + delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att) + return True + if (att == "member" and flag == FLAG_MOD_REPLACE): hash = {} newval = [] @@ -825,7 +846,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): + if handle_special_case(att, delta, reference, current, usns, basedn, samdb): # This attribute is "complicated" to handle and handling # was done in handle_special_case continue @@ -883,7 +904,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): + usns, basedn, samdb): if opts.debugchange or opts.debugall: try: dump_denied_change(dn, att, -- cgit From fcdf619b361b9c30b59f65ba38f69f344b4c95ae Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 15 Jul 2010 14:00:48 +1000 Subject: s4:pyldb Fix memory handling for ldb_message_element The problem here is that we need to use the array, not the individual message element as the memory context. Andrew Bartlett --- source4/lib/ldb/pyldb.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'source4') diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c index f1a02c90b1..bcfbedd962 100644 --- a/source4/lib/ldb/pyldb.c +++ b/source4/lib/ldb/pyldb.c @@ -1658,9 +1658,14 @@ struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx, { struct ldb_message_element *me; - if (PyLdbMessageElement_Check(set_obj)) - return talloc_reference(mem_ctx, - PyLdbMessageElement_AsMessageElement(set_obj)); + if (PyLdbMessageElement_Check(set_obj)) { + PyLdbMessageElementObject *set_obj_as_me = (PyLdbMessageElementObject *)set_obj; + /* We have to talloc_reference() the memory context, not the pointer which may not actually be it's own context */ + if (talloc_reference(mem_ctx, set_obj_as_me->mem_ctx)) { + return PyLdbMessageElement_AsMessageElement(set_obj); + } + return NULL; + } me = talloc(mem_ctx, struct ldb_message_element); @@ -1964,7 +1969,7 @@ static PyObject *py_ldb_msg_getitem_helper(PyLdbMessageObject *self, PyObject *p if (el == NULL) { return NULL; } - return (PyObject *)PyLdbMessageElement_FromMessageElement(el, msg); + return (PyObject *)PyLdbMessageElement_FromMessageElement(el, msg->elements); } static PyObject *py_ldb_msg_getitem(PyLdbMessageObject *self, PyObject *py_name) @@ -2003,7 +2008,7 @@ static PyObject *py_ldb_msg_items(PyLdbMessageObject *self) j++; } for (i = 0; i < msg->num_elements; i++, j++) { - PyList_SetItem(l, j, Py_BuildValue("(sO)", msg->elements[i].name, PyLdbMessageElement_FromMessageElement(&msg->elements[i], self->msg))); + PyList_SetItem(l, j, Py_BuildValue("(sO)", msg->elements[i].name, PyLdbMessageElement_FromMessageElement(&msg->elements[i], msg->elements))); } return l; } -- cgit From 3e8dba17030544a389611814e47521ceafa1ae8a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 15 Jul 2010 14:01:56 +1000 Subject: s4:pyldb whitespace fix --- source4/lib/ldb/pyldb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c index bcfbedd962..f27ab3dd95 100644 --- a/source4/lib/ldb/pyldb.c +++ b/source4/lib/ldb/pyldb.c @@ -2046,7 +2046,7 @@ static int py_ldb_msg_setitem(PyLdbMessageObject *self, PyObject *name, PyObject ldb_msg_remove_attr(self->msg, attr_name); } else { struct ldb_message_element *el = PyObject_AsMessageElement(self->msg, - value, 0, attr_name); + value, 0, attr_name); if (el == NULL) return -1; ldb_msg_remove_attr(PyLdbMessage_AsMessage(self), attr_name); -- cgit From 299057d8d97cce349af2ff931396fae1f09493a5 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 15 Jul 2010 14:03:11 +1000 Subject: s4:provision Handle machine account password changes while keeping keytab The challenge here is to update the existing record if it already exists, rather than deleting the old record. This ensures that the secrets.keytab handling code keeps the previous password in the keytab. Andrew Bartlett --- source4/scripting/python/samba/provision.py | 38 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'source4') diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index 5ede869015..8be3f655d2 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -680,17 +680,16 @@ def secretsdb_self_join(secretsdb, domain, "krb5Keytab", "privateKeytab"] - + #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain)) - msg["secureChannelType"] = str(secure_channel_type) - msg["flatname"] = [domain] + msg["secureChannelType"] = [str(secure_channel_type)] msg["objectClass"] = ["top", "primaryDomain"] if realm is not None: if dnsdomain is None: dnsdomain = realm.lower() msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"] - msg["realm"] = realm - msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper()) + msg["realm"] = [realm] + msg["saltPrincipal"] = ["host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())] msg["msDS-KeyVersionNumber"] = [str(key_version_number)] msg["privateKeytab"] = ["secrets.keytab"] @@ -701,30 +700,39 @@ def secretsdb_self_join(secretsdb, domain, if domainsid is not None: msg["objectSid"] = [ndr_pack(domainsid)] + # This complex expression tries to ensure that we don't have more + # than one record for this SID, realm or netbios domain at a time, + # but we don't delete the old record that we are about to modify, + # because that would delete the keytab and previous password. res = secretsdb.search(base="cn=Primary Domains", attrs=attrs, - expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), + expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))), scope=ldb.SCOPE_ONELEVEL) for del_msg in res: - if del_msg.dn is not msg.dn: secretsdb.delete(del_msg.dn) res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE) if len(res) == 1: - msg["priorSecret"] = res[0]["secret"] - msg["priorWhenChanged"] = res[0]["whenChanged"] + msg["priorSecret"] = [res[0]["secret"][0]] + msg["priorWhenChanged"] = [res[0]["whenChanged"][0]] - if res["privateKeytab"] is not None: - msg["privateKeytab"] = res[0]["privateKeytab"] + try: + msg["privateKeytab"] = [res[0]["privateKeytab"][0]] + except KeyError: + pass - if res["krb5Keytab"] is not None: - msg["krb5Keytab"] = res[0]["krb5Keytab"] + try: + msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]] + except KeyError: + pass for el in msg: - el.set_flags(ldb.FLAG_MOD_REPLACE) - secretsdb.modify(msg) + if el != 'dn': + msg[el].set_flags(ldb.FLAG_MOD_REPLACE) + secretsdb.modify(msg) + secretsdb.rename(res[0].dn, msg.dn) else: secretsdb.add(msg) -- cgit From 0e212acd32b0378e9d7ea912f0eee5afa230d898 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 15 Jul 2010 16:45:13 +1000 Subject: s4:testprogs Operate the blackbox kinit and net tests using the :local config This :local tells selftest.pl to use the local smb.conf for the test environment, not the generic client smb.conf This then makes the rest work properly - otherwise, it may attempt to connect to the wrong KDC for example. The only problem is that we can't test the 'net join' with this set, so this is removed from the test. The member server test environment checks this anyway. Andrew Bartlett --- source4/selftest/tests.sh | 10 +++++----- source4/utils/tests/test_net.sh | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'source4') diff --git a/source4/selftest/tests.sh b/source4/selftest/tests.sh index 20e0a6af29..52d1d3963e 100755 --- a/source4/selftest/tests.sh +++ b/source4/selftest/tests.sh @@ -363,11 +363,11 @@ planperltestsuite "selftest.samba4.pl" none $samba4srcdir/../selftest/test_samba # work correctly. plantestsuite "blackbox.ndrdump" none $samba4srcdir/librpc/tests/test_ndrdump.sh -plantestsuite "blackbox.net" dc $samba4srcdir/utils/tests/test_net.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" -plantestsuite "blackbox.pkinit" dc $bbdir/test_pkinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $CONFIGURATION -plantestsuite "blackbox.kinit" dc $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $CONFIGURATION -plantestsuite "blackbox.kinit" fl2000dc $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" arcfour-hmac-md5 $CONFIGURATION -plantestsuite "blackbox.kinit" fl2008r2dc $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $CONFIGURATION +plantestsuite "blackbox.net" dc:local $samba4srcdir/utils/tests/test_net.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" +plantestsuite "blackbox.pkinit" dc:local $bbdir/test_pkinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $CONFIGURATION +plantestsuite "blackbox.kinit" dc:local $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $CONFIGURATION +plantestsuite "blackbox.kinit" fl2000dc:local $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" arcfour-hmac-md5 $CONFIGURATION +plantestsuite "blackbox.kinit" fl2008r2dc:local $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" aes256-cts-hmac-sha1-96 $CONFIGURATION plantestsuite "blackbox.passwords" dc:local $bbdir/test_passwords.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" plantestsuite "blackbox.export.keytab" dc:local $bbdir/test_export_keytab.sh "\$SERVER" "\$USERNAME" "\$REALM" "\$DOMAIN" "$PREFIX" plantestsuite "blackbox.cifsdd" dc $samba4srcdir/client/tests/test_cifsdd.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" diff --git a/source4/utils/tests/test_net.sh b/source4/utils/tests/test_net.sh index e49989ff81..09394f41be 100755 --- a/source4/utils/tests/test_net.sh +++ b/source4/utils/tests/test_net.sh @@ -29,8 +29,6 @@ testit() { return $status } -testit "domain join" $VALGRIND $net join $DOMAIN $CONFIGURATION -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@ - testit "Test login with --machine-pass without kerberos" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp --machine-pass -k no testit "Test login with --machine-pass and kerberos" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp --machine-pass -k yes -- cgit