s4-drs: Fixes bugs regarding Urgent Replication on wrong situations
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index 890eb91d6d4d90ad3e0b8552126afb9f252e4f40..51611aca1d3e5ea8c7b26de729cd8882f931e571 100644 (file)
@@ -48,6 +48,7 @@
 #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
 
@@ -60,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;
 };
 
@@ -85,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
@@ -353,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;
+                       }
                }
        }
 
@@ -383,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);
 
@@ -391,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)));
@@ -539,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;
 }
@@ -577,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,
@@ -668,11 +729,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 */
@@ -888,11 +951,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;
@@ -911,7 +980,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,
@@ -994,7 +1063,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;
@@ -1003,9 +1073,11 @@ 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;
+       enum urgent_situation situation;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -1026,7 +1098,20 @@ static int replmd_update_rpmd(struct ldb_module *module,
                         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) {
@@ -1051,11 +1136,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 && (situation == REPL_URGENT_ON_UPDATE)) {
+                       *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
+               }
+
        }
 
        /*
@@ -1101,7 +1191,6 @@ static int replmd_update_rpmd(struct ldb_module *module,
        return LDB_SUCCESS;     
 }
 
-
 struct parsed_dn {
        struct dsdb_dn *dsdb_dn;
        struct GUID *guid;
@@ -1196,7 +1285,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;
 }
@@ -1920,6 +2009,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)) {
@@ -1946,7 +2036,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        ldb_msg_remove_attr(msg, "whenChanged");
        ldb_msg_remove_attr(msg, "uSNChanged");
 
-       ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
+       ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t, &is_urgent);
        if (ret != LDB_SUCCESS) {
                talloc_free(ac);
                return ret;
@@ -1962,6 +2052,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,
@@ -2667,10 +2759,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]);
@@ -3064,9 +3158,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
@@ -3696,10 +3788,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) {