repl_meta_data: Remove the correct forward link for dn+binary attributes
authorAndrew Bartlett <abartlet@samba.org>
Mon, 12 Oct 2015 02:51:37 +0000 (15:51 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 14 Feb 2017 05:14:35 +0000 (06:14 +0100)
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 <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Tue Feb 14 06:14:35 CET 2017 on sn-devel-144

selftest/knownfail
source4/dsdb/samdb/ldb_modules/repl_meta_data.c

index 1f659c8b39fcfa8cae686eae75e5f28349385349..d96e238796c34521e60fb3b386f5ae0f6175f5ec 100644 (file)
 ^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.*
index a85ac866e8f8d576d7c9dc906577387a067a316b..e3bf0324d7cf5ef022dff5742009f55467bf1f31 100644 (file)
@@ -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; i<el->num_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; i<old_msg->num_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;
                                }