X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fdsdb%2Fsamdb%2Fldb_modules%2Flinked_attributes.c;h=324faa2c2a91cd5421c11ca3d02129e2aa20f4d7;hb=87f31510475c6debd56ff874130f4f5d48bef9a5;hp=f54693d8091f9846d8a5460cdab3c2b3d03b7609;hpb=56b887e5b94efedd0203550f6bc21d767da5d6c6;p=samba.git diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c index f54693d8091..324faa2c2a9 100644 --- a/source4/dsdb/samdb/ldb_modules/linked_attributes.c +++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c @@ -1,4 +1,4 @@ -/* +/* ldb database library Copyright (C) Andrew Bartlett 2007 @@ -8,12 +8,12 @@ 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 . */ @@ -30,9 +30,10 @@ #include "includes.h" #include "ldb_module.h" -#include "dlinklist.h" +#include "util/dlinklist.h" #include "dsdb/samdb/samdb.h" #include "librpc/gen_ndr/ndr_misc.h" +#include "dsdb/samdb/ldb_modules/util.h" struct la_private { struct la_context *la_list; @@ -58,7 +59,6 @@ struct la_context { const struct dsdb_schema *schema; struct ldb_module *module; struct ldb_request *req; - struct ldb_dn *partition_dn; struct ldb_dn *add_dn; struct ldb_dn *del_dn; struct replace_context *rc; @@ -72,7 +72,6 @@ static struct la_context *linked_attributes_init(struct ldb_module *module, { struct ldb_context *ldb; struct la_context *ac; - const struct ldb_control *partition_ctrl; ldb = ldb_module_get_ctx(module); @@ -82,21 +81,10 @@ static struct la_context *linked_attributes_init(struct ldb_module *module, return NULL; } - ac->schema = dsdb_get_schema(ldb); + ac->schema = dsdb_get_schema(ldb, ac); ac->module = module; ac->req = req; - /* remember the partition DN that came in, if given */ - partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID); - if (partition_ctrl) { - const struct dsdb_control_current_partition *partition; - partition = talloc_get_type(partition_ctrl->data, - struct dsdb_control_current_partition); - SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION); - - ac->partition_dn = ldb_dn_copy(ac, partition->dn); - } - return ac; } @@ -105,17 +93,17 @@ static struct la_context *linked_attributes_init(struct ldb_module *module, */ static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid) { - int ret; NTSTATUS status; + int ret; - status = dsdb_get_extended_dn_guid(dn, guid); + status = dsdb_get_extended_dn_guid(dn, guid, "GUID"); if (NT_STATUS_IS_OK(status)) { return LDB_SUCCESS; } if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n", ldb_dn_get_linearized(dn))); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_operr(ldb_module_get_ctx(ac->module)); } ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid); @@ -143,20 +131,20 @@ static int la_store_op(struct la_context *ac, op_dn = ldb_dn_from_ldb_val(ac, ldb, dn); if (!op_dn) { - ldb_asprintf_errstring(ldb, + ldb_asprintf_errstring(ldb, "could not parse attribute as a DN"); return LDB_ERR_INVALID_DN_SYNTAX; } os = talloc_zero(ac, struct la_op_store); if (!os) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } os->op = op; ret = la_guid_from_dn(ac, op_dn, &os->guid); + talloc_free(op_dn); if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) { /* we are deleting an object, and we've found it has a * forward link to a target that no longer @@ -173,8 +161,7 @@ static int la_store_op(struct la_context *ac, os->name = talloc_strdup(os, name); if (!os->name) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } /* Do deletes before adds */ @@ -189,8 +176,6 @@ static int la_store_op(struct la_context *ac, return LDB_SUCCESS; } -static int la_op_search_callback(struct ldb_request *req, - struct ldb_reply *ares); static int la_queue_mod_request(struct la_context *ac); static int la_down_req(struct la_context *ac); @@ -203,8 +188,9 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request * const struct dsdb_attribute *target_attr; struct la_context *ac; const char *attr_name; + struct ldb_control *ctrl; + unsigned int i, j; int ret; - int i, j; ldb = ldb_module_get_ctx(module); @@ -213,9 +199,15 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request * return ldb_next_request(module, req); } + if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) { + /* don't do anything special for linked attributes, repl_meta_data has done it */ + return ldb_next_request(module, req); + } + ctrl->critical = false; + ac = linked_attributes_init(module, req); if (!ac) { - return LDB_ERR_OPERATIONS_ERROR; + return ldb_operr(ldb); } if (!ac->schema) { @@ -230,22 +222,24 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request * const struct dsdb_attribute *schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); if (!schema_attr) { - ldb_asprintf_errstring(ldb, - "attribute %s is not a valid attribute in schema", el->name); - return LDB_ERR_OBJECT_CLASS_VIOLATION; + ldb_asprintf_errstring(ldb, + "%s: attribute %s is not a valid attribute in schema", + __FUNCTION__, + el->name); + return LDB_ERR_OBJECT_CLASS_VIOLATION; } - /* We have a valid attribute, now find out if it is linked */ - if (schema_attr->linkID == 0) { + /* We have a valid attribute, now find out if it is a forward link */ + if ((schema_attr->linkID == 0)) { continue; } - + if ((schema_attr->linkID & 1) == 1) { - /* Odd is for the target. Illegal to modify */ - ldb_asprintf_errstring(ldb, - "attribute %s must not be modified directly, it is a linked attribute", el->name); - return LDB_ERR_UNWILLING_TO_PERFORM; + unsigned int functional_level; + + functional_level = dsdb_functional_level(ldb); + SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000); } - + /* Even link IDs are for the originating attribute */ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); if (!target_attr) { @@ -295,7 +289,7 @@ static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *are struct replace_context *rc; struct la_context *ac; const char *attr_name; - int i, j; + unsigned int i, j; int ret = LDB_SUCCESS; ac = talloc_get_type(req->context, struct la_context); @@ -316,8 +310,9 @@ static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *are case LDB_REPLY_ENTRY: if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) { - ldb_asprintf_errstring(ldb, - "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn)); + ldb_asprintf_errstring(ldb, + "linked_attributes: %s is not the DN we were looking for", + ldb_dn_get_linearized(ares->message->dn)); /* Guh? We only asked for this DN */ talloc_free(ares); return ldb_module_done(ac->req, NULL, NULL, @@ -332,7 +327,8 @@ static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *are schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name); if (!schema_attr) { ldb_asprintf_errstring(ldb, - "attribute %s is not a valid attribute in schema", + "%s: attribute %s is not a valid attribute in schema", + __FUNCTION__, rc->el[i].name); talloc_free(ares); return ldb_module_done(ac->req, NULL, NULL, @@ -417,11 +413,11 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques /* Apply the modify to the linked entry */ struct ldb_context *ldb; - int i, j; + unsigned int i, j; struct la_context *ac; struct ldb_request *search_req; const char **attrs; - + struct ldb_control *ctrl; int ret; ldb = ldb_module_get_ctx(module); @@ -431,9 +427,15 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques return ldb_next_request(module, req); } + if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) { + /* don't do anything special for linked attributes, repl_meta_data has done it */ + return ldb_next_request(module, req); + } + ctrl->critical = false; + ac = linked_attributes_init(module, req); if (!ac) { - return LDB_ERR_OPERATIONS_ERROR; + return ldb_operr(ldb); } if (!ac->schema) { @@ -443,8 +445,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques ac->rc = talloc_zero(ac, struct replace_context); if (!ac->rc) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } for (i=0; i < req->op.mod.message->num_elements; i++) { @@ -455,24 +456,24 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques const struct dsdb_attribute *schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); if (!schema_attr) { - ldb_asprintf_errstring(ldb, - "attribute %s is not a valid attribute in schema", el->name); - return LDB_ERR_OBJECT_CLASS_VIOLATION; + ldb_asprintf_errstring(ldb, + "%s: attribute %s is not a valid attribute in schema", + __FUNCTION__, + el->name); + return LDB_ERR_OBJECT_CLASS_VIOLATION; } - /* We have a valid attribute, now find out if it is linked */ + /* We have a valid attribute, now find out if it is a forward link + (Even link IDs are for the originating attribute) */ if (schema_attr->linkID == 0) { continue; } - + if ((schema_attr->linkID & 1) == 1) { - /* Odd is for the target. Illegal to modify */ - ldb_asprintf_errstring(ldb, - "attribute %s must not be modified directly, it is a linked attribute", el->name); - return LDB_ERR_UNWILLING_TO_PERFORM; + unsigned int functional_level; + + functional_level = dsdb_functional_level(ldb); + SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000); } - - /* Even link IDs are for the originating attribute */ - /* Now find the target attribute */ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); if (!target_attr) { @@ -487,7 +488,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques } attr_name = target_attr->lDAPDisplayName; - + switch (el->flags & LDB_FLAG_MOD_MASK) { case LDB_FLAG_MOD_REPLACE: /* treat as just a normal add the delete part is handled by the callback */ @@ -537,8 +538,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques struct ldb_message_element, ac->rc->num_elements +1); if (!search_el) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } ac->rc->el = search_el; @@ -546,21 +546,20 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques ac->rc->num_elements++; } } - + if (ac->ops || ac->rc->el) { /* both replace and delete without values are handled in the callback * after the search on the entry to be modified is performed */ - + attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1); if (!attrs) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } for (i = 0; ac->rc && i < ac->rc->num_elements; i++) { attrs[i] = ac->rc->el[i].name; } attrs[i] = NULL; - + /* The callback does all the hard work here */ ret = ldb_build_search_req(&search_req, ldb, ac, req->op.mod.message->dn, @@ -569,6 +568,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques NULL, ac, la_mod_search_callback, req); + LDB_REQ_SET_LOCATION(search_req); /* We need to figure out our own extended DN, to fill in as the backlink target */ if (ret == LDB_SUCCESS) { @@ -578,7 +578,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques } if (ret == LDB_SUCCESS) { talloc_steal(search_req, attrs); - + ret = ldb_next_request(module, search_req); } @@ -591,249 +591,184 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques return ret; } -/* delete */ -static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req) +static int linked_attributes_fix_links(struct ldb_module *module, + struct ldb_dn *old_dn, struct ldb_dn *new_dn, + struct ldb_message_element *el, struct dsdb_schema *schema, + const struct dsdb_attribute *schema_attr, + struct ldb_request *parent) { - struct ldb_context *ldb; - struct ldb_request *search_req; - struct la_context *ac; - const char **attrs; - WERROR werr; + unsigned int i, j; + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_context *ldb = ldb_module_get_ctx(module); + const struct dsdb_attribute *target; + const char *attrs[2]; int ret; - /* This gets complex: We need to: - - Do a search for the entry - - Wait for these result to appear - - In the callback for the result, issue a modify - request based on the linked attributes found - - Wait for each modify result - - Regain our sainity - */ - - ldb = ldb_module_get_ctx(module); - - ac = linked_attributes_init(module, req); - if (!ac) { - return LDB_ERR_OPERATIONS_ERROR; - } - - if (!ac->schema) { - /* without schema, this doesn't make any sense */ - return ldb_next_request(module, req); - } - - werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs); - if (!W_ERROR_IS_OK(werr)) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&search_req, ldb, req, - req->op.del.dn, LDB_SCOPE_BASE, - "(objectClass=*)", attrs, - NULL, - ac, la_op_search_callback, - req); - - if (ret != LDB_SUCCESS) { - return ret; - } - - talloc_steal(search_req, attrs); - - return ldb_next_request(module, search_req); -} - -/* rename */ -static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req) -{ - struct la_context *ac; - - /* This gets complex: We need to: - - Do a search for the entry - - Wait for these result to appear - - In the callback for the result, issue a modify - request based on the linked attributes found - - Wait for each modify result - - Regain our sainity - */ - - ac = linked_attributes_init(module, req); - if (!ac) { - return LDB_ERR_OPERATIONS_ERROR; - } - - if (!ac->schema) { - /* without schema, this doesn't make any sense */ - return ldb_next_request(module, req); + target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1); + if (target == NULL) { + /* there is no counterpart link to change */ + return LDB_SUCCESS; } - /* start with the original request */ - return la_down_req(ac); -} - - -static int la_op_search_callback(struct ldb_request *req, - struct ldb_reply *ares) -{ - struct ldb_context *ldb; - struct la_context *ac; - const struct dsdb_attribute *schema_attr; - const struct dsdb_attribute *target_attr; - const struct ldb_message_element *el; - const char *attr_name; - int i, j; - int ret; - - ac = talloc_get_type(req->context, struct la_context); - ldb = ldb_module_get_ctx(ac->module); + attrs[0] = target->lDAPDisplayName; + attrs[1] = NULL; - if (!ares) { - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ac->req, ares->controls, - ares->response, ares->error); - } + for (i=0; inum_values; i++) { + struct dsdb_dn *dsdb_dn; + struct ldb_result *res; + struct ldb_message *msg; + struct ldb_message_element *el2; - /* Only entries are interesting, and we only want the olddn */ - switch (ares->type) { - case LDB_REPLY_ENTRY: - ret = ldb_dn_compare(ares->message->dn, req->op.search.base); - if (ret != 0) { - /* Guh? We only asked for this DN */ - talloc_free(ares); - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); + dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid); + if (dsdb_dn == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_INVALID_DN_SYNTAX; } - if (ares->message->num_elements == 0) { - /* only bother at all if there were some - * linked attributes found */ - talloc_free(ares); - return LDB_SUCCESS; + + ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn, + attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | + DSDB_SEARCH_REVEAL_INTERNALS, parent); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s", + el->name, target->lDAPDisplayName, + ldb_dn_get_linearized(old_dn), + ldb_dn_get_linearized(dsdb_dn->dn), + ldb_errstring(ldb)); + talloc_free(tmp_ctx); + return ret; } + msg = res->msgs[0]; - switch (ac->req->operation) { - case LDB_DELETE: - ac->del_dn = talloc_steal(ac, ares->message->dn); - break; - case LDB_RENAME: - ac->add_dn = talloc_steal(ac, ares->message->dn); - ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn); - break; - default: - talloc_free(ares); - ldb_set_errstring(ldb, - "operations must be delete or rename"); - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); + if (msg->num_elements == 0) { + /* Forward link without backlink remaining - nothing to do here */ + continue; + } else if (msg->num_elements != 1) { + ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s", + msg->num_elements, ldb_dn_get_linearized(msg->dn)); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; } + if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) { + ldb_asprintf_errstring(ldb, "Bad returned attribute in linked_attributes_fix_links: got %s, expected %s for %s", msg->elements[0].name, target->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + el2 = &msg->elements[0]; - for (i = 0; i < ares->message->num_elements; i++) { - el = &ares->message->elements[i]; + el2->flags = LDB_FLAG_MOD_REPLACE; - schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); - if (!schema_attr) { - ldb_asprintf_errstring(ldb, - "attribute %s is not a valid attribute" - " in schema", el->name); - talloc_free(ares); - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OBJECT_CLASS_VIOLATION); + /* find our DN in the values */ + for (j=0; jnum_values; j++) { + struct dsdb_dn *dsdb_dn2; + dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid); + if (dsdb_dn2 == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_INVALID_DN_SYNTAX; } - - /* Valid attribute, now find out if it is linked */ - if (schema_attr->linkID == 0) { - /* Not a linked attribute, skip */ + if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) { continue; } - - if ((schema_attr->linkID & 1) == 0) { - /* Odd is for the target. */ - target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); - if (!target_attr) { - continue; - } - attr_name = target_attr->lDAPDisplayName; - } else { - target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1); - if (!target_attr) { - continue; - } - attr_name = target_attr->lDAPDisplayName; + ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; } - for (j = 0; j < el->num_values; j++) { - ret = la_store_op(ac, LA_OP_DEL, - &el->values[j], - attr_name); - /* for renames, ensure we add it back */ - if (ret == LDB_SUCCESS - && ac->req->operation == LDB_RENAME) { - ret = la_store_op(ac, LA_OP_ADD, - &el->values[j], - attr_name); - } - if (ret != LDB_SUCCESS) { - talloc_free(ares); - return ldb_module_done(ac->req, - NULL, NULL, ret); - } - } + el2->values[j] = data_blob_string_const( + dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1)); } - break; + ret = dsdb_check_single_valued_link(target, el2); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } - case LDB_REPLY_REFERRAL: - /* ignore */ - break; + /* we may be putting multiple values in an attribute - + disable checking for this attribute */ + el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK; - case LDB_REPLY_DONE: + ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s", + el->name, target->lDAPDisplayName, + ldb_dn_get_linearized(old_dn), + ldb_dn_get_linearized(dsdb_dn->dn), + ldb_errstring(ldb)); + talloc_free(tmp_ctx); + return ret; + } + } - talloc_free(ares); + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} - switch (ac->req->operation) { - case LDB_DELETE: - /* start the mod requests chain */ - ret = la_down_req(ac); - if (ret != LDB_SUCCESS) { - return ldb_module_done(ac->req, NULL, NULL, ret); - } - return ret; +/* rename */ +static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_result *res; + struct ldb_message *msg; + unsigned int i; + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct dsdb_schema *schema; + int ret; + /* + - load the current msg + - find any linked attributes + - if its a link then find the target object + - modify the target linked attributes with the new DN + */ + ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn, + NULL, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED, req); + if (ret != LDB_SUCCESS) { + return ret; + } - case LDB_RENAME: - /* start the mod requests chain */ - ret = la_queue_mod_request(ac); - if (ret != LDB_SUCCESS) { - return ldb_module_done(ac->req, NULL, NULL, - ret); - } + schema = dsdb_get_schema(ldb, res); + if (!schema) { + return ldb_oom(ldb); + } + + msg = res->msgs[0]; + + for (i=0; inum_elements; i++) { + struct ldb_message_element *el = &msg->elements[i]; + const struct dsdb_attribute *schema_attr + = dsdb_attribute_by_lDAPDisplayName(schema, el->name); + if (!schema_attr || schema_attr->linkID == 0) { + continue; + } + ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el, + schema, schema_attr, req); + if (ret != LDB_SUCCESS) { + talloc_free(res); return ret; - - default: - talloc_free(ares); - ldb_set_errstring(ldb, - "operations must be delete or rename"); - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); } } - talloc_free(ares); - return LDB_SUCCESS; + talloc_free(res); + + return ldb_next_request(module, req); } + /* queue a linked attributes modify request in the la_private structure */ static int la_queue_mod_request(struct la_context *ac) { - struct la_private *la_private = + struct la_private *la_private = talloc_get_type(ldb_module_get_private(ac->module), struct la_private); if (la_private == NULL) { ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n"); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_operr(ldb_module_get_ctx(ac->module)); } talloc_steal(la_private, ac); @@ -846,9 +781,9 @@ static int la_queue_mod_request(struct la_context *ac) /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */ static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) { - int ret; struct la_context *ac; struct ldb_context *ldb; + int ret; ac = talloc_get_type(req->context, struct la_context); ldb = ldb_module_get_ctx(ac->module); @@ -869,13 +804,13 @@ static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - + ac->op_controls = talloc_steal(ac, ares->controls); ac->op_response = talloc_steal(ac, ares->response); /* If we have modfies to make, this is the time to do them for modify and delete */ ret = la_queue_mod_request(ac); - + if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } @@ -886,79 +821,15 @@ static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) } -/* Having done the original rename try to fix up all the linked attributes */ -static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares) -{ - int ret; - struct la_context *ac; - struct ldb_request *search_req; - const char **attrs; - WERROR werr; - struct ldb_context *ldb; - - ac = talloc_get_type(req->context, struct la_context); - ldb = ldb_module_get_ctx(ac->module); - - if (!ares) { - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ac->req, ares->controls, - ares->response, ares->error); - } - - if (ares->type != LDB_REPLY_DONE) { - ldb_set_errstring(ldb, - "invalid ldb_reply_type in callback"); - talloc_free(ares); - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - - werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs); - if (!W_ERROR_IS_OK(werr)) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&search_req, ldb, req, - ac->req->op.rename.newdn, LDB_SCOPE_BASE, - "(objectClass=*)", attrs, - NULL, - ac, la_op_search_callback, - req); - - if (ret != LDB_SUCCESS) { - return ret; - } - - talloc_steal(search_req, attrs); - - if (ret == LDB_SUCCESS) { - ret = ldb_request_add_control(search_req, - LDB_CONTROL_EXTENDED_DN_OID, - false, NULL); - } - if (ret != LDB_SUCCESS) { - return ldb_module_done(ac->req, NULL, NULL, - ret); - } - - ac->op_controls = talloc_steal(ac, ares->controls); - ac->op_response = talloc_steal(ac, ares->response); - - return ldb_next_request(ac->module, search_req); -} - /* Having done the original add, then try to fix up all the linked attributes This is done after the add so the links can get the extended DNs correctly. */ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) { - int ret; struct la_context *ac; struct ldb_context *ldb; + int ret; ac = talloc_get_type(req->context, struct la_context); ldb = ldb_module_get_ctx(ac->module); @@ -979,11 +850,11 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - + if (ac->ops) { struct ldb_request *search_req; static const char *attrs[] = { NULL }; - + /* The callback does all the hard work here - we need * the objectGUID and SID of the added record */ ret = ldb_build_search_req(&search_req, ldb, ac, @@ -993,7 +864,8 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) NULL, ac, la_mod_search_callback, ac->req); - + LDB_REQ_SET_LOCATION(search_req); + if (ret == LDB_SUCCESS) { ret = ldb_request_add_control(search_req, LDB_CONTROL_EXTENDED_DN_OID, @@ -1008,7 +880,7 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) ac->op_response = talloc_steal(ac, ares->response); return ldb_next_request(ac->module, search_req); - + } else { return ldb_module_done(ac->req, ares->controls, ares->response, ares->error); @@ -1019,8 +891,8 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) static int la_down_req(struct la_context *ac) { struct ldb_request *down_req; - int ret; struct ldb_context *ldb; + int ret; ldb = ldb_module_get_ctx(ac->module); @@ -1031,6 +903,7 @@ static int la_down_req(struct la_context *ac) ac->req->controls, ac, la_add_callback, ac->req); + LDB_REQ_SET_LOCATION(down_req); break; case LDB_MODIFY: ret = ldb_build_mod_req(&down_req, ldb, ac, @@ -1038,21 +911,7 @@ static int la_down_req(struct la_context *ac) ac->req->controls, ac, la_mod_del_callback, ac->req); - break; - case LDB_DELETE: - ret = ldb_build_del_req(&down_req, ldb, ac, - ac->req->op.del.dn, - ac->req->controls, - ac, la_mod_del_callback, - ac->req); - break; - case LDB_RENAME: - ret = ldb_build_rename_req(&down_req, ldb, ac, - ac->req->op.rename.olddn, - ac->req->op.rename.newdn, - ac->req->controls, - ac, la_rename_callback, - ac->req); + LDB_REQ_SET_LOCATION(down_req); break; default: ret = LDB_ERR_OPERATIONS_ERROR; @@ -1068,17 +927,16 @@ static int la_down_req(struct la_context *ac) use the GUID part of an extended DN to find the target DN, in case it has moved */ -static int la_find_dn_target(struct ldb_module *module, struct la_context *ac, +static int la_find_dn_target(struct ldb_module *module, struct la_context *ac, struct GUID *guid, struct ldb_dn **dn) { - return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn); + return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn); } /* apply one la_context op change */ static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op) { struct ldb_message_element *ret_el; - struct ldb_request *mod_req; struct ldb_message *new_msg; struct ldb_context *ldb; int ret; @@ -1088,8 +946,7 @@ static int la_do_op_request(struct ldb_module *module, struct la_context *ac, st /* Create the modify request */ new_msg = ldb_msg_new(ac); if (!new_msg) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn); @@ -1109,8 +966,7 @@ static int la_do_op_request(struct ldb_module *module, struct la_context *ac, st } ret_el->values = talloc_array(new_msg, struct ldb_val, 1); if (!ret_el->values) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb); } ret_el->num_values = 1; if (op->op == LA_OP_ADD) { @@ -1121,38 +977,19 @@ static int la_do_op_request(struct ldb_module *module, struct la_context *ac, st #if 0 ldb_debug(ldb, LDB_DEBUG_WARNING, - "link on %s %s: %s %s\n", - ldb_dn_get_linearized(new_msg->dn), ret_el->name, + "link on %s %s: %s %s\n", + ldb_dn_get_linearized(new_msg->dn), ret_el->name, ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted"); -#endif - - ret = ldb_build_mod_req(&mod_req, ldb, op, - new_msg, - NULL, - NULL, - ldb_op_default_callback, - NULL); - if (ret != LDB_SUCCESS) { - return ret; - } - talloc_steal(mod_req, new_msg); +#endif if (DEBUGLVL(4)) { DEBUG(4,("Applying linked attribute change:\n%s\n", ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg))); } - /* Run the new request */ - ret = ldb_next_request(module, mod_req); - - /* we need to wait for this to finish, as we are being called - from the synchronous end_transaction hook of this module */ - if (ret == LDB_SUCCESS) { - ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL); - } - + ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req); if (ret != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n", + ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n", ldb_errstring(ldb), ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)); } @@ -1179,7 +1016,7 @@ static int la_do_mod_request(struct ldb_module *module, struct la_context *ac) /* - we hook into the transaction operations to allow us to + we hook into the transaction operations to allow us to perform the linked attribute updates at the end of the whole transaction. This allows a forward linked attribute to be created before the target is created, as long as the target is created @@ -1193,7 +1030,7 @@ static int linked_attributes_start_transaction(struct ldb_module *module) talloc_free(la_private); la_private = talloc(module, struct la_private); if (la_private == NULL) { - return LDB_ERR_OPERATIONS_ERROR; + return ldb_oom(ldb_module_get_ctx(module)); } la_private->la_list = NULL; ldb_module_set_private(module, la_private); @@ -1206,7 +1043,7 @@ static int linked_attributes_start_transaction(struct ldb_module *module) */ static int linked_attributes_prepare_commit(struct ldb_module *module) { - struct la_private *la_private = + struct la_private *la_private = talloc_get_type(ldb_module_get_private(module), struct la_private); struct la_context *ac; @@ -1217,29 +1054,33 @@ static int linked_attributes_prepare_commit(struct ldb_module *module) /* walk the list backwards, to do the first entry first, as we * added the entries with DLIST_ADD() which puts them at the * start of the list */ - for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ; - for (; ac; ac=ac->prev) { + /* Start at the end of the list - so we can start + * there, but ensure we don't create a loop by NULLing + * it out in the first element */ + ac = DLIST_TAIL(la_private->la_list); + + for (; ac; ac=DLIST_PREV(ac)) { int ret; ac->req = NULL; ret = la_do_mod_request(module, ac); if (ret != LDB_SUCCESS) { DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret)); talloc_free(la_private); - ldb_module_set_private(module, NULL); + ldb_module_set_private(module, NULL); return ret; } } talloc_free(la_private); - ldb_module_set_private(module, NULL); + ldb_module_set_private(module, NULL); return ldb_next_prepare_commit(module); } static int linked_attributes_del_transaction(struct ldb_module *module) { - struct la_private *la_private = + struct la_private *la_private = talloc_get_type(ldb_module_get_private(module), struct la_private); talloc_free(la_private); ldb_module_set_private(module, NULL); @@ -1247,13 +1088,18 @@ static int linked_attributes_del_transaction(struct ldb_module *module) } -_PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = { +static const struct ldb_module_ops ldb_linked_attributes_module_ops = { .name = "linked_attributes", .add = linked_attributes_add, .modify = linked_attributes_modify, - .del = linked_attributes_del, .rename = linked_attributes_rename, .start_transaction = linked_attributes_start_transaction, .prepare_commit = linked_attributes_prepare_commit, .del_transaction = linked_attributes_del_transaction, }; + +int ldb_linked_attributes_module_init(const char *version) +{ + LDB_MODULE_CHECK_VERSION(version); + return ldb_register_module(&ldb_linked_attributes_module_ops); +}