util: rewrite dlinklist.h so that DLIST_ADD_END() is O(1)
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index bf4a55054a079766f2d4c455b01d846da32c2f4f..67d7094d4c890e7c9bc8f8889b0b2b628c51727d 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
 
@@ -59,6 +60,7 @@ struct replmd_private {
                struct nc_entry *prev, *next;
                struct ldb_dn *dn;
                uint64_t mod_usn;
+               uint64_t mod_usn_urgent;
        } *ncs;
 };
 
@@ -84,11 +86,68 @@ struct replmd_replicated_request {
        struct ldb_message *search_msg;
 
        uint64_t seq_num;
+       bool is_urgent;
+};
 
+enum urgent_situation {
+       REPL_URGENT_ON_CREATE = 1,
+       REPL_URGENT_ON_UPDATE = 3, /* activated on creating as well*/
+       REPL_URGENT_ON_DELETE = 4
 };
 
-static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
 
+static const struct {
+       const char *update_name;
+       enum urgent_situation repl_situation;
+} urgent_objects[] = {
+               {"nTDSDSA", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
+               {"crossRef", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE)},
+               {"attributeSchema", REPL_URGENT_ON_UPDATE},
+               {"classSchema", REPL_URGENT_ON_UPDATE},
+               {"secret", REPL_URGENT_ON_UPDATE},
+               {"rIDManager", REPL_URGENT_ON_UPDATE},
+               {NULL, 0}
+};
+
+/* Attributes looked for when updating or deleting, to check for a urgent replication needed */
+static const char *urgent_attrs[] = {
+               "lockoutTime",
+               "pwdLastSet",
+               "userAccountControl",
+               NULL
+};
+
+
+static bool replmd_check_urgent_objectclass(const struct ldb_message_element *objectclass_el,
+                                       enum urgent_situation situation)
+{
+       int i, j;
+       for (i=0; urgent_objects[i].update_name; i++) {
+
+               if ((situation & urgent_objects[i].repl_situation) == 0) {
+                       continue;
+               }
+
+               for (j=0; j<objectclass_el->num_values; j++) {
+                       const struct ldb_val *v = &objectclass_el->values[j];
+                       if (ldb_attr_cmp((const char *)v->data, urgent_objects[i].update_name) == 0) {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+static bool replmd_check_urgent_attribute(const struct ldb_message_element *el)
+{
+       if (ldb_attr_in_list(urgent_attrs, el->name)) {
+               return true;
+       }
+       return false;
+}
+
+
+static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
 
 /*
   initialise the module
@@ -216,7 +275,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 +377,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);
        }
@@ -351,6 +411,9 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
 
                if (ac->seq_num > modified_partition->mod_usn) {
                        modified_partition->mod_usn = ac->seq_num;
+                       if (ac->is_urgent) {
+                               modified_partition->mod_usn_urgent = ac->seq_num;
+                       }
                }
        }
 
@@ -381,7 +444,7 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
  */
 static int replmd_notify_store(struct ldb_module *module)
 {
-       struct replmd_private *replmd_private = 
+       struct replmd_private *replmd_private =
                talloc_get_type(ldb_module_get_private(module), struct replmd_private);
        struct ldb_context *ldb = ldb_module_get_ctx(module);
 
@@ -389,7 +452,9 @@ static int replmd_notify_store(struct ldb_module *module)
                int ret;
                struct nc_entry *modified_partition = replmd_private->ncs;
 
-               ret = dsdb_save_partition_usn(ldb, modified_partition->dn, modified_partition->mod_usn);
+               ret = dsdb_save_partition_usn(ldb, modified_partition->dn,
+                                               modified_partition->mod_usn,
+                                               modified_partition->mod_usn_urgent);
                if (ret != LDB_SUCCESS) {
                        DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
                                 ldb_dn_get_linearized(modified_partition->dn)));
@@ -608,6 +673,22 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                NTSTATUS status;
                int 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) || 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) {
@@ -615,14 +696,6 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                        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)) {
-                       talloc_free(tmp_ctx);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-
                ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
@@ -658,11 +731,13 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        uint32_t i, ni=0;
        bool allow_add_guid = false;
        bool remove_current_guid = false;
+       bool is_urgent = false;
+       struct ldb_message_element *objectclass_el;
 
         /* 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 */
@@ -878,11 +953,17 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
         */
        replmd_ldb_message_sort(msg, ac->schema);
 
+       objectclass_el = ldb_msg_find_element(msg, "objectClass");
+       is_urgent = replmd_check_urgent_objectclass(objectclass_el,
+                                                       REPL_URGENT_ON_CREATE);
+
+       ac->is_urgent = is_urgent;
        ret = ldb_build_add_req(&down_req, ldb, ac,
                                msg,
                                req->controls,
                                ac, replmd_op_callback,
                                req);
+
        if (ret != LDB_SUCCESS) {
                talloc_free(ac);
                return ret;
@@ -901,7 +982,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
 /*
  * update the replPropertyMetaData for one element
  */
-static int replmd_update_rpmd_element(struct ldb_context *ldb, 
+static int replmd_update_rpmd_element(struct ldb_context *ldb,
                                      struct ldb_message *msg,
                                      struct ldb_message_element *el,
                                      struct replPropertyMetaDataBlob *omd,
@@ -984,7 +1065,8 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
 static int replmd_update_rpmd(struct ldb_module *module, 
                              const struct dsdb_schema *schema, 
                              struct ldb_message *msg, uint64_t *seq_num,
-                             time_t t)
+                             time_t t,
+                             bool *is_urgent)
 {
        const struct ldb_val *omd_value;
        enum ndr_err_code ndr_err;
@@ -993,9 +1075,10 @@ static int replmd_update_rpmd(struct ldb_module *module,
        NTTIME now;
        const struct GUID *our_invocation_id;
        int ret;
-       const char *attrs[] = { "replPropertyMetaData" , NULL };
+       const char *attrs[] = { "replPropertyMetaData" , "objectClass", NULL };
        struct ldb_result *res;
        struct ldb_context *ldb;
+       struct ldb_message_element *objectclass_el;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -1016,7 +1099,12 @@ static int replmd_update_rpmd(struct ldb_module *module,
                         ldb_dn_get_linearized(msg->dn)));
                return LDB_ERR_OPERATIONS_ERROR;
        }
-               
+
+       objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
+       if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
+                                                       REPL_URGENT_ON_UPDATE)) {
+               *is_urgent = true;
+       }
 
        omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
        if (!omd_value) {
@@ -1041,11 +1129,16 @@ static int replmd_update_rpmd(struct ldb_module *module,
        }
 
        for (i=0; i<msg->num_elements; i++) {
-               ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num, 
+               ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
                                                 our_invocation_id, now);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
+
+               if (is_urgent && !*is_urgent) {
+                       *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
+               }
+
        }
 
        /*
@@ -1091,7 +1184,6 @@ static int replmd_update_rpmd(struct ldb_module *module,
        return LDB_SUCCESS;     
 }
 
-
 struct parsed_dn {
        struct dsdb_dn *dsdb_dn;
        struct GUID *guid;
@@ -1103,9 +1195,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 +1260,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 +1286,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
@@ -1191,15 +1299,16 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
                               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) {
@@ -1220,6 +1329,9 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
        local_usnv = data_blob_string_const(usn_string);
 
        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);
@@ -1227,13 +1339,13 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
                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;
@@ -1299,17 +1411,18 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
                                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;
        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) {
@@ -1334,13 +1447,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 */
@@ -1439,7 +1552,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);
@@ -1458,9 +1571,9 @@ 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);
@@ -1569,16 +1682,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;
@@ -1590,14 +1703,14 @@ 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, 0, true);
@@ -1684,10 +1797,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) {
@@ -1695,7 +1807,7 @@ 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;
@@ -1717,7 +1829,7 @@ 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,
@@ -1864,7 +1976,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
@@ -1890,6 +2002,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        struct ldb_message *msg;
        time_t t = time(NULL);
        int ret;
+       bool is_urgent = false;
 
        /* do not manipulate our control entries */
        if (ldb_dn_is_special(req->op.mod.message->dn)) {
@@ -1913,7 +2026,10 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
+       ldb_msg_remove_attr(msg, "whenChanged");
+       ldb_msg_remove_attr(msg, "uSNChanged");
+
+       ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t, &is_urgent);
        if (ret != LDB_SUCCESS) {
                talloc_free(ac);
                return ret;
@@ -1929,6 +2045,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
         * - replace the old object with the newly constructed one
         */
 
+       ac->is_urgent = is_urgent;
+
        ret = ldb_build_mod_req(&down_req, ldb, ac,
                                msg,
                                req->controls,
@@ -2072,8 +2190,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,
@@ -2136,7 +2256,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;
@@ -2176,11 +2296,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);
@@ -2197,6 +2321,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) {
@@ -2262,6 +2400,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,
@@ -2296,12 +2447,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)) {
@@ -2336,8 +2488,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);
@@ -2539,10 +2691,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),
@@ -2601,10 +2752,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]);
@@ -3313,23 +3466,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 
@@ -3430,41 +3577,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));
-               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));
+               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;
        }
 
-       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");
@@ -3490,7 +3636,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) {
@@ -3637,10 +3783,8 @@ static int replmd_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 (la = replmd_private->la_list; la && la->next; la=la->next) ;
-
-       for (; la; la=prev) {
-               prev = la->prev;
+       for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
+               prev = DLIST_PREV(la);
                DLIST_REMOVE(replmd_private->la_list, la);
                ret = replmd_process_linked_attribute(module, la);
                if (ret != LDB_SUCCESS) {