s4:repl_meta_data - Transform a "1" into a "true" on a boolean variable
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index 7f797752d0d8aadde6b1589bfce689fb3d0e50fb..b4caac4c8db8fd1d5c0c6135b242e94e7e7fd7c4 100644 (file)
@@ -47,6 +47,7 @@
 #include "lib/util/dlinklist.h"
 #include "dsdb/samdb/ldb_modules/util.h"
 #include "lib/util/binsearch.h"
+#include "libcli/security/security.h"
 
 #define W2K3_LINKED_ATTRIBUTES 1
 
@@ -216,7 +217,7 @@ static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_sche
        struct replmd_private *replmd_private =
                talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
 
-       target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
+       target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
        if (!target_attr) {
                /*
                 * windows 2003 has a broken schema where the
@@ -318,6 +319,7 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
        }
 
        if (!partition_ctrl) {
+               ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
                return ldb_module_done(ac->req, NULL,
                                       NULL, LDB_ERR_OPERATIONS_ERROR);
        }
@@ -581,7 +583,7 @@ static void replmd_ldb_message_sort(struct ldb_message *msg,
 
 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
                               const struct GUID *invocation_id, uint64_t seq_num,
-                              uint64_t local_usn, NTTIME nttime, bool deleted);
+                              uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
 
 
 /*
@@ -608,19 +610,27 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                NTSTATUS status;
                int ret;
 
-               ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
-                                         seq_num, seq_num, now, false);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(tmp_ctx);
-                       return ret;
-               }
-
                /* note that the DN already has the extended
                   components from the extended_dn_store module */
                status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
-               if (!NT_STATUS_IS_OK(status)) {
+               if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
+                       ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+                       ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+               }
+
+               ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
+                                         seq_num, seq_num, now, 0, false);
+               if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return ret;
                }
 
                ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
@@ -662,7 +672,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
         /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
         control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
        if (control) {
-               allow_add_guid = 1;
+               allow_add_guid = true;
        }
 
        /* do not manipulate our control entries */
@@ -1103,9 +1113,21 @@ static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
        return GUID_compare(pdn1->guid, pdn2->guid);
 }
 
-static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid)
+static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
 {
        struct parsed_dn *ret;
+       if (dn && GUID_all_zero(guid)) {
+               /* when updating a link using DRS, we sometimes get a
+                  NULL GUID. We then need to try and match by DN */
+               int i;
+               for (i=0; i<count; i++) {
+                       if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
+                               dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
+                               return &pdn[i];
+                       }
+               }
+               return NULL;
+       }
        BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
        return ret;
 }
@@ -1156,12 +1178,16 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
                if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
                        /* we got a DN without a GUID - go find the GUID */
-                       int ret = dsdb_find_guid_by_dn(ldb, dn, p->guid);
+                       int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
                        if (ret != LDB_SUCCESS) {
                                ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
                                                       ldb_dn_get_linearized(dn));
                                return ret;
                        }
+                       ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
                } else if (!NT_STATUS_IS_OK(status)) {
                        return LDB_ERR_OPERATIONS_ERROR;
                }
@@ -1178,7 +1204,7 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
 /*
   build a new extended DN, including all meta data fields
 
-  DELETED             = 1 or missing
+  RMD_FLAGS           = DSDB_RMD_FLAG_* bits
   RMD_ADDTIME         = originating_add_time
   RMD_INVOCID         = originating_invocation_id
   RMD_CHANGETIME      = originating_change_time
@@ -1188,17 +1214,19 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
  */
 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
                               const struct GUID *invocation_id, uint64_t seq_num,
-                              uint64_t local_usn, NTTIME nttime, bool deleted)
+                              uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
 {
        struct ldb_dn *dn = dsdb_dn->dn;
-       const char *tstring, *usn_string;
+       const char *tstring, *usn_string, *flags_string;
        struct ldb_val tval;
        struct ldb_val iid;
        struct ldb_val usnv, local_usnv;
-       struct ldb_val vers;
+       struct ldb_val vers, flagsv;
        NTSTATUS status;
        int ret;
        const char *dnstring;
+       char *vstring;
+       uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
 
        tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
        if (!tstring) {
@@ -1218,20 +1246,24 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
        }
        local_usnv = data_blob_string_const(usn_string);
 
-       vers = data_blob_string_const("0");
+       vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
+       if (!vstring) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       vers = data_blob_string_const(vstring);
 
        status = GUID_to_ndr_blob(invocation_id, dn, &iid);
        if (!NT_STATUS_IS_OK(status)) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       if (deleted) {
-               struct ldb_val dv;
-               dv = data_blob_string_const("1");
-               ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
-       } else {
-               ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
+       flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
+       if (!flags_string) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
+       flagsv = data_blob_string_const(flags_string);
+
+       ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
        if (ret != LDB_SUCCESS) return ret;
        ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
        if (ret != LDB_SUCCESS) return ret;
@@ -1257,7 +1289,8 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
 
 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
                                struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
-                               uint64_t seq_num, uint64_t local_usn, NTTIME nttime, bool deleted);
+                               uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
+                               uint32_t version, bool deleted);
 
 /*
   check if any links need upgrading from w2k format
@@ -1277,7 +1310,7 @@ static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, con
 
                /* it's an old one that needs upgrading */
                ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
-                                          1, 1, 0, false);
+                                          1, 1, 0, 0, false);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
@@ -1292,18 +1325,22 @@ static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, con
  */
 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
                                struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
-                               uint64_t seq_num, uint64_t local_usn, NTTIME nttime, bool deleted)
+                               uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
+                               uint32_t version, bool deleted)
 {
        struct ldb_dn *dn = dsdb_dn->dn;
-       const char *tstring, *usn_string;
+       const char *tstring, *usn_string, *flags_string;
        struct ldb_val tval;
        struct ldb_val iid;
        struct ldb_val usnv, local_usnv;
-       struct ldb_val vers;
-       const struct ldb_val *old_addtime, *old_version;
+       struct ldb_val vers, flagsv;
+       const struct ldb_val *old_addtime;
+       uint32_t old_version;
        NTSTATUS status;
        int ret;
        const char *dnstring;
+       char *vstring;
+       uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
 
        tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
        if (!tstring) {
@@ -1328,13 +1365,13 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       if (deleted) {
-               struct ldb_val dv;
-               dv = data_blob_string_const("1");
-               ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
-       } else {
-               ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
+       flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
+       if (!flags_string) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
+       flagsv = data_blob_string_const(flags_string);
+
+       ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
        if (ret != LDB_SUCCESS) return ret;
 
        /* get the ADDTIME from the original */
@@ -1363,19 +1400,12 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
        if (ret != LDB_SUCCESS) return ret;
 
        /* increase the version by 1 */
-       old_version = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_VERSION");
-       if (old_version == NULL) {
-               vers = data_blob_string_const("0");
-       } else {
-               char *vstring;
-               vstring = talloc_strndup(dn, (const char *)old_version->data, old_version->length);
-               if (!vstring) {
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               vstring = talloc_asprintf(dn, "%lu",
-                                         (unsigned long)strtoul(vstring, NULL, 0)+1);
-               vers = data_blob_string_const(vstring);
+       status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
+       if (NT_STATUS_IS_OK(status) && old_version >= version) {
+               version = old_version+1;
        }
+       vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
+       vers = data_blob_string_const(vstring);
        ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
        if (ret != LDB_SUCCESS) return ret;
 
@@ -1440,7 +1470,7 @@ static int replmd_modify_la_add(struct ldb_module *module,
 
        /* for each new value, see if it exists already with the same GUID */
        for (i=0; i<el->num_values; i++) {
-               struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid);
+               struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
                if (p == NULL) {
                        /* this is a new linked attribute value */
                        new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
@@ -1450,7 +1480,7 @@ static int replmd_modify_la_add(struct ldb_module *module,
                                return LDB_ERR_OPERATIONS_ERROR;
                        }
                        ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
-                                                 invocation_id, seq_num, seq_num, now, false);
+                                                 invocation_id, seq_num, seq_num, now, 0, false);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -1459,16 +1489,16 @@ static int replmd_modify_la_add(struct ldb_module *module,
                } else {
                        /* this is only allowed if the GUID was
                           previously deleted. */
-                       const struct ldb_val *v;
-                       v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
-                       if (v == NULL) {
+                       uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+
+                       if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
                                ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
                                                       el->name, GUID_string(tmp_ctx, p->guid));
                                talloc_free(tmp_ctx);
                                return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
                        }
                        ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
-                                                  invocation_id, seq_num, seq_num, now, false);
+                                                  invocation_id, seq_num, seq_num, now, 0, false);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -1570,16 +1600,16 @@ static int replmd_modify_la_delete(struct ldb_module *module,
        for (i=0; i<el->num_values; i++) {
                struct parsed_dn *p = &dns[i];
                struct parsed_dn *p2;
-               const struct ldb_val *v;
+               uint32_t rmd_flags;
 
-               p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid);
+               p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
                if (!p2) {
                        ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
                                               el->name, GUID_string(tmp_ctx, p->guid));
                        return LDB_ERR_NO_SUCH_ATTRIBUTE;
                }
-               v = ldb_dn_get_extended_component(p2->dsdb_dn->dn, "DELETED");
-               if (v) {
+               rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
+               if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
                        ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
                                               el->name, GUID_string(tmp_ctx, p->guid));
                        return LDB_ERR_NO_SUCH_ATTRIBUTE;
@@ -1591,17 +1621,17 @@ static int replmd_modify_la_delete(struct ldb_module *module,
        */
        for (i=0; i<old_el->num_values; i++) {
                struct parsed_dn *p = &old_dns[i];
-               const struct ldb_val *v;
+               uint32_t rmd_flags;
 
-               if (dns && parsed_dn_find(dns, el->num_values, p->guid) == NULL) {
+               if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
                        continue;
                }
 
-               v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
-               if (v != NULL) continue;
+               rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+               if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
 
                ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
-                                          invocation_id, seq_num, seq_num, now, true);
+                                          invocation_id, seq_num, seq_num, now, 0, true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -1685,10 +1715,9 @@ static int replmd_modify_la_replace(struct ldb_module *module,
        for (i=0; i<old_num_values; i++) {
                struct parsed_dn *old_p = &old_dns[i];
                struct parsed_dn *p;
-               const struct ldb_val *v;
+               uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
 
-               v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
-               if (v) continue;
+               if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
 
                ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
                if (ret != LDB_SUCCESS) {
@@ -1696,14 +1725,14 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                        return ret;
                }
 
-               p = parsed_dn_find(dns, el->num_values, old_p->guid);
+               p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
                if (p) {
                        /* we don't delete it if we are re-adding it */
                        continue;
                }
 
                ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
-                                          invocation_id, seq_num, seq_num, now, true);
+                                          invocation_id, seq_num, seq_num, now, 0, true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -1718,11 +1747,11 @@ static int replmd_modify_la_replace(struct ldb_module *module,
 
                if (old_dns &&
                    (old_p = parsed_dn_find(old_dns,
-                                           old_num_values, p->guid)) != NULL) {
+                                           old_num_values, p->guid, NULL)) != NULL) {
                        /* update in place */
                        ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
                                                   old_p->dsdb_dn, invocation_id,
-                                                  seq_num, seq_num, now, false);
+                                                  seq_num, seq_num, now, 0, false);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -1737,7 +1766,7 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                                return LDB_ERR_OPERATIONS_ERROR;
                        }
                        ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
-                                                 invocation_id, seq_num, seq_num, now, false);
+                                                 invocation_id, seq_num, seq_num, now, 0, false);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -1865,7 +1894,7 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
                }
                ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
                new_el->num_values = el->num_values;
-               new_el->values = el->values;
+               new_el->values = talloc_steal(msg->elements, el->values);
 
                /* TODO: this relises a bit too heavily on the exact
                   behaviour of ldb_msg_find_element and
@@ -1914,6 +1943,9 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
+       ldb_msg_remove_attr(msg, "whenChanged");
+       ldb_msg_remove_attr(msg, "uSNChanged");
+
        ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
        if (ret != LDB_SUCCESS) {
                talloc_free(ac);
@@ -2073,8 +2105,10 @@ 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 */
+/*
+   remove links from objects that point at this object when an object
+   is deleted
+ */
 static int replmd_delete_remove_link(struct ldb_module *module,
                                     struct dsdb_schema *schema,
                                     struct ldb_dn *dn,
@@ -2137,7 +2171,7 @@ static int replmd_delete_remove_link(struct ldb_module *module,
                el2->values = &dn_val;
                el2->num_values = 1;
 
-               ret = dsdb_module_modify(module, msg, 0);
+               ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2177,11 +2211,15 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
                "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
                "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
                "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
-               "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreate",
-               NULL};
+               "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
+               "whenChanged", NULL};
        uint32_t el_count = 0;
        int i;
 
+       if (ldb_dn_is_special(req->op.del.dn)) {
+               return ldb_next_request(module, req);
+       }
+
        tmp_ctx = talloc_new(ldb);
 
        old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
@@ -2198,6 +2236,20 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
        }
        old_msg = res->msgs[0];
 
+       if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
+               struct auth_session_info *session_info =
+                       (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+               if (security_session_user_level(session_info) != SECURITY_SYSTEM) {
+                       ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
+                                              ldb_dn_get_linearized(old_msg->dn));
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               }
+
+               /* it is already deleted - really remove it this time */
+               talloc_free(tmp_ctx);
+               return ldb_next_request(module, req);
+       }
+
        /* 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) {
@@ -2263,6 +2315,19 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
        }
        msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
 
+       /* we also mark it as recycled, meaning this object can't be
+          recovered (we are stripping its attributes) */
+       if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
+               ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
+               if (ret != LDB_SUCCESS) {
+                       DEBUG(0,(__location__ ": Failed to add isRecycled 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,
@@ -2297,12 +2362,13 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
                        continue;
                }
 
-               if (sa->linkID) {
+               if (sa->linkID && sa->linkID & 1) {
                        ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return LDB_ERR_OPERATIONS_ERROR;
                        }
+                       continue;
                }
 
                if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
@@ -2337,8 +2403,8 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
                el->flags = LDB_FLAG_MOD_REPLACE;
        }
 
-       ret = dsdb_module_modify(module, msg, 0);
-       if (ret != LDB_SUCCESS){
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
+       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);
@@ -2540,10 +2606,9 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
                          ldb_dn_get_linearized(ar->search_msg->dn),
                          ldb_dn_get_linearized(msg->dn));
-               /* we can't use dsdb_module_rename() here as we need
-                  the rename call to be intercepted by this module, to
-                  allow it to process linked attribute changes */
-               if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
+               if (dsdb_module_rename(ar->module,
+                                      ar->search_msg->dn, msg->dn,
+                                      DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
                        ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
                                  ldb_dn_get_linearized(ar->search_msg->dn),
                                  ldb_dn_get_linearized(msg->dn),
@@ -2602,10 +2667,12 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                                break;
                        }
 
-                       DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
-                                msg->elements[i-removed_attrs].name,
-                                ldb_dn_get_linearized(msg->dn),
-                                GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
+                       if (rmd->ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) {
+                               DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
+                                        msg->elements[i-removed_attrs].name,
+                                        ldb_dn_get_linearized(msg->dn),
+                                        GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
+                       }
 
                        /* we don't want to apply this change so remove the attribute */
                        ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
@@ -3314,23 +3381,17 @@ static int replmd_process_linked_attribute(struct ldb_module *module,
        const struct dsdb_attribute *attr;
        struct dsdb_dn *dsdb_dn;
        uint64_t seq_num = 0;
-       struct drsuapi_DsReplicaAttribute drs;
-       struct drsuapi_DsAttributeValue val;
-       struct ldb_message_element new_el, *old_el;
+       struct ldb_message_element *old_el;
        WERROR status;
        time_t t = time(NULL);
        struct ldb_result *res;
        const char *attrs[2];
        struct parsed_dn *pdn_list, *pdn;
-       struct GUID guid;
+       struct GUID guid = GUID_zero();
        NTSTATUS ntstatus;
        bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
        const struct GUID *our_invocation_id;
 
-       drs.value_ctr.num_values = 1;
-       drs.value_ctr.values = &val;
-       val.blob = la->value.blob;
-
 /*
 linked_attributes[0]:                                                     
      &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute 
@@ -3431,41 +3492,40 @@ linked_attributes[0]:
                return ret;
        }
 
-       status = attr->syntax->drsuapi_to_ldb(ldb, schema, attr, &drs, tmp_ctx, &new_el);
+       status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
        if (!W_ERROR_IS_OK(status)) {
-               ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s\n",
-                                      old_el->name, ldb_dn_get_linearized(msg->dn));
+               ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
+                                      old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       if (new_el.num_values != 1) {
-               ldb_asprintf_errstring(ldb, "Failed to find value in linked attribute blob for %s on %s\n",
-                                      old_el->name, ldb_dn_get_linearized(msg->dn));
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &new_el.values[0], attr->syntax->ldap_oid);
-       if (!dsdb_dn) {
-               ldb_asprintf_errstring(ldb, "Failed to parse DN in linked attribute blob for %s on %s\n",
-                                      old_el->name, ldb_dn_get_linearized(msg->dn));
+       ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
+       if (!NT_STATUS_IS_OK(ntstatus) && active) {
+               ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
+                                      old_el->name,
+                                      ldb_dn_get_linearized(dsdb_dn->dn),
+                                      ldb_dn_get_linearized(msg->dn));
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
-       if (!NT_STATUS_IS_OK(ntstatus)) {
-               ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s\n",
-                                      old_el->name, ldb_dn_get_linearized(msg->dn));
-               return LDB_ERR_OPERATIONS_ERROR;
+       /* re-resolve the DN by GUID, as the DRS server may give us an
+          old DN value */
+       ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, __location__ ": Failed to re-resolve GUID %s",
+                                      GUID_string(tmp_ctx, &guid));
+               talloc_free(tmp_ctx);
+               return ret;
        }
 
        /* see if this link already exists */
-       pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid);
+       pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
        if (pdn != NULL) {
                /* see if this update is newer than what we have already */
                struct GUID invocation_id = GUID_zero();
                uint32_t version = 0;
                NTTIME change_time = 0;
-               bool was_active = ldb_dn_get_extended_component(pdn->dsdb_dn->dn, "DELETED") == NULL;
+               uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
 
                dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
                dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
@@ -3491,7 +3551,7 @@ linked_attributes[0]:
                        return ret;
                }
 
-               if (was_active) {
+               if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
                        /* remove the existing backlink */
                        ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
                        if (ret != LDB_SUCCESS) {
@@ -3504,6 +3564,7 @@ linked_attributes[0]:
                                           &la->meta_data.originating_invocation_id,
                                           la->meta_data.originating_usn, seq_num,
                                           la->meta_data.originating_change_time,
+                                          la->meta_data.version,
                                           !active);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
@@ -3538,6 +3599,7 @@ linked_attributes[0]:
                                          &la->meta_data.originating_invocation_id,
                                          la->meta_data.originating_usn, seq_num,
                                          la->meta_data.originating_change_time,
+                                         la->meta_data.version,
                                          (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
@@ -3566,9 +3628,15 @@ linked_attributes[0]:
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = dsdb_module_modify(module, msg, 0);
+       ret = dsdb_check_single_valued_link(attr, old_el);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
        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, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
                talloc_free(tmp_ctx);