s4:repl_meta_data LDB module - change counter variables to "unsigned" where appropriate
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index 9994b9566dfa6f227072ddbe7c568f813ad0d2e2..e96a2059a66a45554c2fdfdf98c53c04a13fb8b0 100644 (file)
@@ -47,6 +47,8 @@
 #include "lib/util/dlinklist.h"
 #include "dsdb/samdb/ldb_modules/util.h"
 #include "lib/util/binsearch.h"
+#include "libcli/security/security.h"
+#include "lib/util/tsort.h"
 
 #define W2K3_LINKED_ATTRIBUTES 1
 
@@ -59,6 +61,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 +87,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 = 2,
+       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_CREATE | REPL_URGENT_ON_UPDATE)},
+               {"classSchema", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
+               {"secret", (REPL_URGENT_ON_CREATE | REPL_URGENT_ON_UPDATE)},
+               {"rIDManager", (REPL_URGENT_ON_CREATE | 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
@@ -352,6 +412,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;
+                       }
                }
        }
 
@@ -382,7 +445,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);
 
@@ -390,7 +453,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)));
@@ -538,9 +603,7 @@ static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1
        DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n", 
                 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
 
-       ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
-                 discard_const_p(void, &rdn_sa->attributeID_id), 
-                 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
+       LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id, replmd_replPropertyMetaData1_attid_sort);
 
        return LDB_SUCCESS;
 }
@@ -576,8 +639,7 @@ static int replmd_ldb_message_element_attid_sort(const struct ldb_message_elemen
 static void replmd_ldb_message_sort(struct ldb_message *msg,
                                    const struct dsdb_schema *schema)
 {
-       ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
-                 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
+       LDB_TYPESAFE_QSORT(msg->elements, msg->num_elements, schema, replmd_ldb_message_element_attid_sort);
 }
 
 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
@@ -594,7 +656,7 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                             uint64_t seq_num, const struct GUID *invocationId, time_t t,
                             struct GUID *guid, const struct dsdb_attribute *sa)
 {
-       int i;
+       unsigned int i;
        TALLOC_CTX *tmp_ctx = talloc_new(el->values);
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct dsdb_schema *schema = dsdb_get_schema(ldb);
@@ -609,13 +671,6 @@ 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, 0, 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");
@@ -625,6 +680,18 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                                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 ret;
                }
 
                ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
@@ -659,14 +726,17 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        NTTIME now;
        char *time_str;
        int ret;
-       uint32_t i, ni=0;
+       unsigned int i;
+       uint32_t 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 */
@@ -882,11 +952,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;
@@ -905,16 +981,17 @@ 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 ldb_message_element *old_el,
                                      struct replPropertyMetaDataBlob *omd,
                                      const struct dsdb_schema *schema,
                                      uint64_t *seq_num,
                                      const struct GUID *our_invocation_id,
                                      NTTIME now)
 {
-       int i;
+       uint32_t i;
        const struct dsdb_attribute *a;
        struct replPropertyMetaData1 *md1;
 
@@ -929,6 +1006,11 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                return LDB_SUCCESS;
        }
 
+       /* if the attribute's value haven't changed then return LDB_SUCCESS     */
+       if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
+               return LDB_SUCCESS;
+       }
+
        for (i=0; i<omd->ctr.ctr1.count; i++) {
                if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
        }
@@ -988,18 +1070,21 @@ 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;
        struct replPropertyMetaDataBlob omd;
-       int i;
+       unsigned int i;
        NTTIME now;
        const struct GUID *our_invocation_id;
        int ret;
-       const char *attrs[] = { "replPropertyMetaData" , NULL };
+       const char *attrs[] = { "replPropertyMetaData", "*", NULL };
        struct ldb_result *res;
        struct ldb_context *ldb;
+       struct ldb_message_element *objectclass_el;
+       enum urgent_situation situation;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -1013,14 +1098,35 @@ static int replmd_update_rpmd(struct ldb_module *module,
 
        unix_to_nt_time(&now, t);
 
-       /* search for the existing replPropertyMetaDataBlob */
-       ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
+       /* search for the existing replPropertyMetaDataBlob. We need
+        * to use REVEAL and ask for DNs in storage format to support
+        * the check for values being the same in
+        * replmd_update_rpmd_element()
+        */
+       ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
+                                   DSDB_SEARCH_SHOW_DELETED |
+                                   DSDB_SEARCH_SHOW_EXTENDED_DN |
+                                   DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
+                                   DSDB_SEARCH_REVEAL_INTERNALS);
        if (ret != LDB_SUCCESS || res->count != 1) {
                DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
                         ldb_dn_get_linearized(msg->dn)));
                return LDB_ERR_OPERATIONS_ERROR;
        }
-               
+
+       /* if isDeleted is present and is TRUE, then we consider we are deleting,
+        * otherwise we consider we are updating */
+       if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
+               situation = REPL_URGENT_ON_DELETE;
+       } else {
+               situation = REPL_URGENT_ON_UPDATE;
+       }
+
+       objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
+       if (is_urgent && replmd_check_urgent_objectclass(objectclass_el,
+                                                       situation)) {
+               *is_urgent = true;
+       }
 
        omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
        if (!omd_value) {
@@ -1045,11 +1151,18 @@ 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, 
+               struct ldb_message_element *old_el;
+               old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
+               ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
                                                 our_invocation_id, now);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
+
+               if (is_urgent && !*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
+                       *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
+               }
+
        }
 
        /*
@@ -1095,7 +1208,6 @@ static int replmd_update_rpmd(struct ldb_module *module,
        return LDB_SUCCESS;     
 }
 
-
 struct parsed_dn {
        struct dsdb_dn *dsdb_dn;
        struct GUID *guid;
@@ -1134,7 +1246,7 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                          struct ldb_message_element *el, struct parsed_dn **pdn,
                          const char *ldap_oid)
 {
-       int i;
+       unsigned int i;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
 
        if (el == NULL) {
@@ -1178,6 +1290,10 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                                                       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;
                }
@@ -1186,7 +1302,7 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                p->v = v;
        }
 
-       qsort(*pdn, el->num_values, sizeof((*pdn)[0]), (comparison_fn_t)parsed_dn_compare);
+       TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
 
        return LDB_SUCCESS;
 }
@@ -1194,7 +1310,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
@@ -1207,15 +1323,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) {
@@ -1236,6 +1353,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);
@@ -1243,13 +1363,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;
@@ -1283,7 +1403,7 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
  */
 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
 {
-       int i;
+       uint32_t i;
        for (i=0; i<count; i++) {
                NTSTATUS status;
                uint32_t version;
@@ -1315,17 +1435,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) {
@@ -1350,13 +1471,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 */
@@ -1416,7 +1537,7 @@ static int replmd_modify_la_add(struct ldb_module *module,
                                time_t t,
                                struct GUID *msg_guid)
 {
-       int i;
+       unsigned int i;
        struct parsed_dn *dns, *old_dns;
        TALLOC_CTX *tmp_ctx = talloc_new(msg);
        int ret;
@@ -1474,9 +1595,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);
@@ -1535,7 +1656,7 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                                   time_t t,
                                   struct GUID *msg_guid)
 {
-       int i;
+       unsigned int i;
        struct parsed_dn *dns, *old_dns;
        TALLOC_CTX *tmp_ctx = talloc_new(msg);
        int ret;
@@ -1585,7 +1706,7 @@ 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, NULL);
                if (!p2) {
@@ -1593,8 +1714,8 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                                               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;
@@ -1606,14 +1727,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 (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);
@@ -1654,15 +1775,15 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                                    time_t t,
                                    struct GUID *msg_guid)
 {
-       int i;
+       unsigned int i;
        struct parsed_dn *dns, *old_dns;
        TALLOC_CTX *tmp_ctx = talloc_new(msg);
        int ret;
        const struct GUID *invocation_id;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_val *new_values = NULL;
-       uint32_t num_new_values = 0;
-       unsigned old_num_values = old_el?old_el->num_values:0;
+       unsigned int num_new_values = 0;
+       unsigned int old_num_values = old_el?old_el->num_values:0;
        NTTIME now;
 
        unix_to_nt_time(&now, t);
@@ -1700,10 +1821,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) {
@@ -1803,7 +1923,8 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
                                               uint64_t seq_num, time_t t)
 {
        struct ldb_result *res;
-       int ret, i;
+       unsigned int i;
+       int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_message *old_msg;
        struct dsdb_schema *schema = dsdb_get_schema(ldb);
@@ -1880,7 +2001,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
@@ -1906,6 +2027,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)) {
@@ -1929,7 +2051,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;
@@ -1945,6 +2070,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,
@@ -2098,7 +2225,7 @@ static int replmd_delete_remove_link(struct ldb_module *module,
                                     struct ldb_message_element *el,
                                     const struct dsdb_attribute *sa)
 {
-       int i;
+       unsigned int i;
        TALLOC_CTX *tmp_ctx = talloc_new(module);
        struct ldb_context *ldb = ldb_module_get_ctx(module);
 
@@ -2195,9 +2322,12 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
                "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
                "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
                "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
-               NULL};
-       uint32_t el_count = 0;
-       int i;
+               "whenChanged", NULL};
+       unsigned int i, el_count = 0;
+
+       if (ldb_dn_is_special(req->op.del.dn)) {
+               return ldb_next_request(module, req);
+       }
 
        tmp_ctx = talloc_new(ldb);
 
@@ -2215,6 +2345,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) {
@@ -2327,12 +2471,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)) {
@@ -2413,7 +2558,7 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
        struct ldb_message *msg;
        struct replPropertyMetaDataBlob *md;
        struct ldb_val md_value;
-       uint32_t i;
+       unsigned int i;
        int ret;
 
        /*
@@ -2553,8 +2698,9 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        const struct ldb_val *omd_value;
        struct replPropertyMetaDataBlob nmd;
        struct ldb_val nmd_value;
-       uint32_t i,j,ni=0;
-       uint32_t removed_attrs = 0;
+       unsigned int i;
+       uint32_t j,ni=0;
+       unsigned int removed_attrs = 0;
        int ret;
 
        ldb = ldb_module_get_ctx(ar->module);
@@ -2570,10 +2716,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),
@@ -2632,10 +2777,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]);
@@ -2894,7 +3041,8 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        struct repsFromToBlob nrf;
        struct ldb_val *nrf_value = NULL;
        struct ldb_message_element *nrf_el = NULL;
-       uint32_t i,j,ni=0;
+       unsigned int i;
+       uint32_t j,ni=0;
        bool found = false;
        time_t t = time(NULL);
        NTTIME now;
@@ -3029,9 +3177,7 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        /*
         * sort the cursors
         */
-       qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
-             sizeof(struct drsuapi_DsReplicaCursor2),
-             (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
+       TYPESAFE_QSORT(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, drsuapi_DsReplicaCursor2_compare);
 
        /*
         * create the change ldb_message
@@ -3244,7 +3390,8 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct
        struct dsdb_extended_replicated_objects *objs;
        struct replmd_replicated_request *ar;
        struct ldb_control **ctrls;
-       int ret, i;
+       int ret;
+       uint32_t i;
        struct replmd_private *replmd_private = 
                talloc_get_type(ldb_module_get_private(module), struct replmd_private);
 
@@ -3344,9 +3491,7 @@ 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;
@@ -3357,10 +3502,6 @@ static int replmd_process_linked_attribute(struct ldb_module *module,
        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 
@@ -3461,23 +3602,10 @@ 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));
-               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));
+               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;
        }
 
@@ -3490,6 +3618,16 @@ linked_attributes[0]:
                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, dsdb_dn->dn);
        if (pdn != NULL) {
@@ -3497,7 +3635,7 @@ linked_attributes[0]:
                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");
@@ -3523,7 +3661,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) {
@@ -3670,10 +3808,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) {