From 4f558c9ad6f9b8a947e33207473579655708eed0 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 12 Oct 2015 15:51:37 +1300 Subject: [PATCH] repl_meta_data: Remove the correct forward link for dn+binary attributes The previous code assumed that only plain DNs could be linked attributes. We need to look over the list of attribute values and find the value that causes this particular backlink to exist, so we can remove it. We do not know (until we search) of the binary portion, so we must search over all the attribute values at this layer, using the parsed_dn_find() routine used elsewhere in this code. Found attempting to demote an RODC in a clone of a Windows 2012R2 domain, due to the msDS-RevealedUsers attribute. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11139 Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Tue Feb 14 06:14:35 CET 2017 on sn-devel-144 --- selftest/knownfail | 2 - source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 100 ++++++++++++++++++++---- 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/selftest/knownfail b/selftest/knownfail index 1f659c8b39f..d96e238796c 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -312,5 +312,3 @@ ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_add_duplicate_different_type.* ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_rank_none.* ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_security_descriptor.* -#we don't properly handle deleting dn+binary linked attributes -^samba4.drs.repl_schema.python.*repl_schema.DrsReplSchemaTestCase.test_classWithCustomBinaryDNLinkAttribute.* diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index a85ac866e8f..e3bf0324d7c 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -3657,7 +3657,9 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are */ static int replmd_delete_remove_link(struct ldb_module *module, const struct dsdb_schema *schema, + struct replmd_private *replmd_private, struct ldb_dn *dn, + struct GUID *guid, struct ldb_message_element *el, const struct dsdb_attribute *sa, struct ldb_request *parent) @@ -3668,14 +3670,19 @@ static int replmd_delete_remove_link(struct ldb_module *module, for (i=0; inum_values; i++) { struct dsdb_dn *dsdb_dn; - NTSTATUS status; int ret; - struct GUID guid2; struct ldb_message *msg; const struct dsdb_attribute *target_attr; struct ldb_message_element *el2; + const char *dn_str; struct ldb_val dn_val; uint32_t dsdb_flags = 0; + const char *attrs[] = { NULL, NULL }; + struct ldb_result *link_res; + struct ldb_message *link_msg; + struct ldb_message_element *link_el; + struct parsed_dn *link_dns; + struct parsed_dn *p = NULL, *unused = NULL; if (dsdb_dn_is_deleted_val(&el->values[i])) { continue; @@ -3687,12 +3694,6 @@ static int replmd_delete_remove_link(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } - status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID"); - 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) { @@ -3708,14 +3709,74 @@ static int replmd_delete_remove_link(struct ldb_module *module, if (target_attr == NULL) { continue; } + attrs[0] = target_attr->lDAPDisplayName; - ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2); + 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)); + + ret = dsdb_module_search_dn(module, tmp_ctx, &link_res, + msg->dn, attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_EXTENDED_DN, + parent); + + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + link_msg = link_res->msgs[0]; + link_el = ldb_msg_find_element(link_msg, + target_attr->lDAPDisplayName); + if (link_el == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + /* + * This call 'upgrades' the links in link_dns, but we + * do not commit the result back into the database, so + * this is safe to call in FL2000 or on databases that + * have been run at that level in the past. + */ + ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, + link_el, &link_dns, + target_attr->syntax->ldap_oid, parent); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ret = parsed_dn_find(ldb, link_dns, link_el->num_values, + guid, dn, &p, &unused, + target_attr->syntax->ldap_oid); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + if (p == NULL) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "Failed to find forward link on %s " + "as %s to remove backlink %s on %s", + ldb_dn_get_linearized(msg->dn), + target_attr->lDAPDisplayName, + sa->lDAPDisplayName, + ldb_dn_get_linearized(dn)); + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + + /* This needs to get the Binary DN, by first searching */ + dn_str = dsdb_dn_get_linearized(tmp_ctx, + p->dsdb_dn); + dn_val = data_blob_string_const(dn_str); el2->values = &dn_val; el2->num_values = 1; @@ -3814,6 +3875,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request }; unsigned int i, el_count = 0; uint32_t dsdb_flags = 0; + struct replmd_private *replmd_private; enum deletion_state deletion_state, next_deletion_state; if (ldb_dn_is_special(req->op.del.dn)) { @@ -3958,10 +4020,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request } } - if (deletion_state == OBJECT_NOT_DELETED) { - /* get the objects GUID from the search we just did */ - guid = samdb_result_guid(old_msg, "objectGUID"); + /* get the objects GUID from the search we just did */ + guid = samdb_result_guid(old_msg, "objectGUID"); + if (deletion_state == OBJECT_NOT_DELETED) { /* Add a formatted child */ retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s", rdn_name, @@ -4125,6 +4187,8 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE; } + replmd_private = talloc_get_type(ldb_module_get_private(module), + struct replmd_private); /* work out which of the old attributes we will be removing */ for (i=0; inum_elements; i++) { const struct dsdb_attribute *sa; @@ -4138,7 +4202,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request /* don't remove the rDN */ continue; } - if (sa->linkID && (sa->linkID & 1)) { + if (sa->linkID & 1) { /* we have a backlink in this object that needs to be removed. We're not @@ -4147,7 +4211,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request modify to delete the corresponding forward link */ - ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req); + ret = replmd_delete_remove_link(module, schema, + replmd_private, + old_dn, &guid, + el, sa, req); if (ret != LDB_SUCCESS) { const char *old_dn_str = ldb_dn_get_linearized(old_dn); @@ -4166,8 +4233,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request directly */ continue; - } - if (!sa->linkID) { + } else if (sa->linkID == 0) { if (ldb_attr_in_list(preserved_attrs, el->name)) { continue; } -- 2.11.4.GIT