From 348bcfc8ff81a95ff2f1785ba4efdaf2e8a193a4 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 16 Dec 2009 17:24:21 +1100 Subject: s4-dsdb: added replmd_delete, based on Eduardos work This implements repmld_delete(), which handles the meta_data updates for an object when deleting. A delete gets mapped to a combination of a rename followed by a modify request, which has the effect of moving the object into the Deleted Objects container. This is based on the code from Eduardo Lima . Eduardo's code was modified to take account of the linked attributes changes that Andrew and I have been working on. Pair-Programmed-With: Andrew Bartlett --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 295 ++++++++++++++++++++++++ 1 file changed, 295 insertions(+) (limited to 'source4/dsdb/samdb/ldb_modules/repl_meta_data.c') diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index dadaf32f80..2a7f472046 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -1914,6 +1914,300 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are return ldb_next_request(ac->module, down_req); } +/* remove forwards and backlinks as needed when an object + is deleted */ +static int replmd_delete_remove_link(struct ldb_module *module, + struct dsdb_schema *schema, + struct ldb_dn *dn, + struct ldb_message_element *el, + const struct dsdb_attribute *sa) +{ + int i; + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_context *ldb = ldb_module_get_ctx(module); + + for (i=0; inum_values; i++) { + struct dsdb_dn *dsdb_dn; + const struct ldb_val *v; + NTSTATUS status; + int ret; + struct GUID guid2; + struct ldb_message *msg; + const struct dsdb_attribute *target_attr; + struct ldb_message_element *el2; + struct ldb_val dn_val; + + if (dsdb_dn_is_deleted_val(&el->values[i])) { + continue; + } + + dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid); + if (!dsdb_dn) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + v = ldb_dn_get_extended_component(dsdb_dn->dn, "GUID"); + if (!v) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + status = GUID_from_ndr_blob(v, &guid2); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* remove the link */ + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ldb_module_oom(module); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + + msg->dn = dsdb_dn->dn; + + if (sa->linkID & 1) { + target_attr = dsdb_attribute_by_linkID(schema, sa->linkID - 1); + } else { + target_attr = dsdb_attribute_by_linkID(schema, sa->linkID + 1); + } + + ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2); + if (ret != LDB_SUCCESS) { + ldb_module_oom(module); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + dn_val = data_blob_string_const(ldb_dn_get_linearized(dn)); + el2->values = &dn_val; + el2->num_values = 1; + + ret = dsdb_module_modify(module, msg, 0); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + } + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + + +/* + handle update of replication meta data for deletion of objects + + This also handles the mapping of delete to a rename operation + to allow deletes to be replicated. + */ +static int replmd_delete(struct ldb_module *module, struct ldb_request *req) +{ + int ret = LDB_ERR_OTHER; + bool retb; + struct ldb_dn *old_dn, *new_dn; + const char *rdn_name; + const struct ldb_val *rdn_value, *new_rdn_value; + struct GUID guid; + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct dsdb_schema *schema = dsdb_get_schema(ldb); + struct ldb_message *msg, *old_msg; + struct ldb_message_element *el; + TALLOC_CTX *tmp_ctx; + struct ldb_result *res, *parent_res; + const char *preserved_attrs[] = { + /* yes, this really is a hard coded list. See MS-ADTS + section 3.1.1.5.5.1.1 */ + "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName", + "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN", + "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID", + "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid", + "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName", + "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection", + "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreate", + NULL}; + uint32_t el_count = 0; + int i; + + tmp_ctx = talloc_new(ldb); + + old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn); + + /* we need the complete msg off disk, so we can work out which + attributes need to be removed */ + ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL, + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_REVEAL_INTERNALS | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + old_msg = res->msgs[0]; + + /* work out where we will be renaming this object to */ + ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn); + if (ret != LDB_SUCCESS) { + /* this is probably an attempted delete on a partition + * that doesn't allow delete operations, such as the + * schema partition */ + ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s", + ldb_dn_get_linearized(old_dn)); + talloc_free(tmp_ctx); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + rdn_name = ldb_dn_get_rdn_name(old_dn); + rdn_value = ldb_dn_get_rdn_val(old_dn); + + /* get the objects GUID from the search we just did */ + guid = samdb_result_guid(old_msg, "objectGUID"); + + /* Add a formatted child */ + retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s", + rdn_name, + rdn_value->data, + GUID_string(tmp_ctx, &guid)); + if (!retb) { + DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s", + ldb_dn_get_linearized(new_dn))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* New DN name generated, renaming the original DN */ + ret = dsdb_module_rename(module, old_dn, new_dn, 0); + if (ret != LDB_SUCCESS){ + DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s'\n", + ldb_dn_get_linearized(old_dn), + ldb_dn_get_linearized(new_dn))); + talloc_free(tmp_ctx); + return ret; + } + + /* + now we need to modify the object in the following ways: + + - add isDeleted=TRUE + - update rDN and name, with new rDN + - remove linked attributes + - remove objectCategory and sAMAccountType + - remove attribs not on the preserved list + - preserved if in above list, or is rDN + - remove all linked attribs from this object + - remove all links from other objects to this object + - add lastKnownParent + - update replPropertyMetaData? + + see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1 + */ + + msg = ldb_msg_new(tmp_ctx); + if (msg == NULL) { + ldb_module_oom(module); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->dn = new_dn; + + ret = ldb_msg_add_string(msg, "isDeleted", "TRUE"); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n")); + ldb_module_oom(module); + talloc_free(tmp_ctx); + return ret; + } + msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD; + + /* we need the storage form of the parent GUID */ + ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res, + ldb_dn_get_parent(tmp_ctx, old_dn), NULL, + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ret = ldb_msg_add_steal_string(msg, "lastKnownParent", + ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1)); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n")); + ldb_module_oom(module); + talloc_free(tmp_ctx); + return ret; + } + msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD; + + /* work out which of the old attributes we will be removing */ + for (i=0; inum_elements; i++) { + const struct dsdb_attribute *sa; + el = &old_msg->elements[i]; + sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name); + if (!sa) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + if (ldb_attr_cmp(el->name, rdn_name) == 0) { + /* don't remove the rDN */ + continue; + } + + if (sa->linkID) { + ret = replmd_delete_remove_link(module, schema, old_dn, el, sa); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) { + continue; + } + + ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + ldb_module_oom(module); + return ret; + } + } + + /* work out what the new rdn value is, for updating the + rDN and name fields */ + new_rdn_value = ldb_dn_get_rdn_val(new_dn); + ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + el->flags = LDB_FLAG_MOD_REPLACE; + + el = ldb_msg_find_element(old_msg, "name"); + if (el) { + ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + el->flags = LDB_FLAG_MOD_REPLACE; + } + + ret = dsdb_module_modify(module, msg, 0); + if (ret != LDB_SUCCESS){ + ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s", + ldb_dn_get_linearized(old_dn), ldb_errstring(ldb)); + talloc_free(tmp_ctx); + return ret; + } + + talloc_free(tmp_ctx); + + return ldb_module_done(req, NULL, NULL, LDB_SUCCESS); +} + + static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret) { @@ -3157,6 +3451,7 @@ _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = { .add = replmd_add, .modify = replmd_modify, .rename = replmd_rename, + .del = replmd_delete, .extended = replmd_extended, .start_transaction = replmd_start_transaction, .prepare_commit = replmd_prepare_commit, -- cgit