dsdb: Add log when ignoring a replicated object outside of partition
[samba.git] / source4 / dsdb / repl / replicated_objects.c
index 88ba598ba6b1a4fcda7746fad35ffcdcebf905d9..fd7491d477fdcb100a0ae5dc0e927a9b50d5fd28 100644 (file)
@@ -31,6 +31,9 @@
 #include "libcli/auth/libcli_auth.h"
 #include "param/param.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS            DBGC_DRS_REPL
+
 static WERROR dsdb_repl_merge_working_schema(struct ldb_context *ldb,
                                             struct dsdb_schema *dest_schema,
                                             const struct dsdb_schema *ref_schema)
@@ -60,7 +63,7 @@ static WERROR dsdb_repl_merge_working_schema(struct ldb_context *ldb,
                 */
                tmp2 = talloc(dest_schema, struct dsdb_class);
                if (tmp2 == NULL) {
-                       return WERR_NOMEM;
+                       return WERR_NOT_ENOUGH_MEMORY;
                }
                *tmp2 = *cur_class;
                DLIST_ADD(dest_schema->classes, tmp2);
@@ -87,7 +90,7 @@ static WERROR dsdb_repl_merge_working_schema(struct ldb_context *ldb,
                 */
                tmp2 = talloc(dest_schema, struct dsdb_attribute);
                if (tmp2 == NULL) {
-                       return WERR_NOMEM;
+                       return WERR_NOT_ENOUGH_MEMORY;
                }
                *tmp2 = *cur_attr;
                DLIST_ADD(dest_schema->attributes, tmp2);
@@ -103,7 +106,6 @@ static WERROR dsdb_repl_merge_working_schema(struct ldb_context *ldb,
 }
 
 WERROR dsdb_repl_resolve_working_schema(struct ldb_context *ldb,
-                                       TALLOC_CTX *mem_ctx,
                                        struct dsdb_schema_prefixmap *pfm_remote,
                                        uint32_t cycle_before_switching,
                                        struct dsdb_schema *initial_schema,
@@ -129,12 +131,13 @@ WERROR dsdb_repl_resolve_working_schema(struct ldb_context *ldb,
                        DRSUAPI_ATTID_systemPossSuperiors,
                        DRSUAPI_ATTID_INVALID
        };
+       TALLOC_CTX *frame = talloc_stackframe();
 
        /* create a list of objects yet to be converted */
        for (cur = first_object; cur; cur = cur->next_object) {
-               schema_list_item = talloc(mem_ctx, struct schema_list);
+               schema_list_item = talloc(frame, struct schema_list);
                if (schema_list_item == NULL) {
-                       return WERR_NOMEM;
+                       return WERR_NOT_ENOUGH_MEMORY;
                }
 
                schema_list_item->obj = cur;
@@ -164,6 +167,7 @@ WERROR dsdb_repl_resolve_working_schema(struct ldb_context *ldb,
                                                        working_schema,
                                                        resulting_schema);
                        if (!W_ERROR_IS_OK(werr)) {
+                               talloc_free(frame);
                                return werr;
                        }
                }
@@ -242,6 +246,7 @@ WERROR dsdb_repl_resolve_working_schema(struct ldb_context *ldb,
                                 "all %d remaining of %d objects "
                                 "failed to convert\n",
                                 failed_obj_count, object_count));
+                       talloc_free(frame);
                        return WERR_INTERNAL_ERROR;
                }
 
@@ -257,12 +262,14 @@ WERROR dsdb_repl_resolve_working_schema(struct ldb_context *ldb,
                        ret = dsdb_setup_sorted_accessors(ldb, working_schema);
                        if (LDB_SUCCESS != ret) {
                                DEBUG(0,("Failed to create schema-cache indexes!\n"));
+                               talloc_free(frame);
                                return WERR_INTERNAL_ERROR;
                        }
                }
                pass_no++;
        }
 
+       talloc_free(frame);
        return WERR_OK;
 }
 
@@ -287,25 +294,62 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
 {
        WERROR werr;
        struct dsdb_schema_prefixmap *pfm_remote;
+       uint32_t r;
        struct dsdb_schema *working_schema;
 
        /* make a copy of the iniatial_scheam so we don't mess with it */
        working_schema = dsdb_schema_copy_shallow(mem_ctx, ldb, initial_schema);
        if (!working_schema) {
                DEBUG(0,(__location__ ": schema copy failed!\n"));
-               return WERR_NOMEM;
+               return WERR_NOT_ENOUGH_MEMORY;
        }
+       working_schema->resolving_in_progress = true;
 
        /* we are going to need remote prefixMap for decoding */
        werr = dsdb_schema_pfm_from_drsuapi_pfm(mapping_ctr, true,
-                                               mem_ctx, &pfm_remote, NULL);
+                                               working_schema, &pfm_remote, NULL);
        if (!W_ERROR_IS_OK(werr)) {
-               DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s",
+               DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
                         win_errstr(werr)));
+               talloc_free(working_schema);
                return werr;
        }
 
-       werr = dsdb_repl_resolve_working_schema(ldb, mem_ctx,
+       for (r=0; r < pfm_remote->length; r++) {
+               const struct dsdb_schema_prefixmap_oid *rm = &pfm_remote->prefixes[r];
+               bool found_oid = false;
+               uint32_t l;
+
+               for (l=0; l < working_schema->prefixmap->length; l++) {
+                       const struct dsdb_schema_prefixmap_oid *lm = &working_schema->prefixmap->prefixes[l];
+                       int cmp;
+
+                       cmp = data_blob_cmp(&rm->bin_oid, &lm->bin_oid);
+                       if (cmp == 0) {
+                               found_oid = true;
+                               break;
+                       }
+               }
+
+               if (found_oid) {
+                       continue;
+               }
+
+               /*
+                * We prefer the same is as we got from the remote peer
+                * if there's no conflict.
+                */
+               werr = dsdb_schema_pfm_add_entry(working_schema->prefixmap,
+                                                rm->bin_oid, &rm->id, NULL);
+               if (!W_ERROR_IS_OK(werr)) {
+                       DEBUG(0,(__location__ ": Failed to merge remote prefixMap: %s",
+                                win_errstr(werr)));
+                       talloc_free(working_schema);
+                       return werr;
+               }
+       }
+
+       werr = dsdb_repl_resolve_working_schema(ldb,
                                                pfm_remote,
                                                0, /* cycle_before_switching */
                                                working_schema,
@@ -315,9 +359,12 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
        if (!W_ERROR_IS_OK(werr)) {
                DEBUG(0, ("%s: dsdb_repl_resolve_working_schema() failed: %s",
                          __location__, win_errstr(werr)));
+               talloc_free(working_schema);
                return werr;
        }
 
+       working_schema->resolving_in_progress = false;
+
        *_schema_out = working_schema;
 
        return WERR_OK;
@@ -574,11 +621,6 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
        if (in->parent_object_guid == NULL) {
                out->parent_guid = NULL;
-               if ((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) {
-                       DEBUG(0, ("Refusing to replicate %s from a server that did not provide a parentGUID!\n",
-                                 ldb_dn_get_linearized(msg->dn)));
-                       return WERR_DS_DRA_INCONSISTENT_DIT;
-               }
        } else {
                out->parent_guid = talloc(mem_ctx, struct GUID);
                W_ERROR_HAVE_NO_MEMORY(out->parent_guid);
@@ -610,6 +652,7 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        struct dsdb_schema_prefixmap *pfm_remote;
        struct dsdb_extended_replicated_objects *out;
        const struct drsuapi_DsReplicaObjectListItemEx *cur;
+       struct dsdb_syntax_ctx syntax_ctx;
        uint32_t i;
 
        out = talloc_zero(mem_ctx, struct dsdb_extended_replicated_objects);
@@ -627,12 +670,16 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        status = dsdb_schema_pfm_from_drsuapi_pfm(mapping_ctr, true,
                                                  out, &pfm_remote, NULL);
        if (!W_ERROR_IS_OK(status)) {
-               DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s",
+               DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
                         win_errstr(status)));
                talloc_free(out);
                return status;
        }
 
+       /* use default syntax conversion context */
+       dsdb_syntax_ctx_init(&syntax_ctx, ldb, schema);
+       syntax_ctx.pfm_remote = pfm_remote;
+
        if (ldb_dn_compare(partition_dn, ldb_get_schema_basedn(ldb)) != 0) {
                /*
                 * check for schema changes in case
@@ -658,11 +705,6 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                                               object_count);
        W_ERROR_HAVE_NO_MEMORY_AND_FREE(out->objects, out);
 
-       /* pass the linked attributes down to the repl_meta_data
-          module */
-       out->linked_attributes_count = linked_attributes_count;
-       out->linked_attributes       = linked_attributes;
-
        for (i=0, cur = first_object; cur; cur = cur->next_object, i++) {
                if (i == object_count) {
                        talloc_free(out);
@@ -685,6 +727,12 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                 * based on the cross-ref object.
                 */
                if (W_ERROR_EQUAL(status, WERR_DS_ADD_REPLICA_INHIBITED)) {
+                       struct GUID_txt_buf guid_str;
+                       DBG_ERR("Ignoring object outside partition %s %s: %s\n",
+                               GUID_buf_string(&cur->object.identifier->guid,
+                                               &guid_str),
+                               cur->object.identifier->dn,
+                               win_errstr(status));
                        continue;
                }
 
@@ -711,6 +759,58 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                return WERR_FOOBAR;
        }
 
+       out->linked_attributes = talloc_array(out,
+                                             struct drsuapi_DsReplicaLinkedAttribute,
+                                             linked_attributes_count);
+       W_ERROR_HAVE_NO_MEMORY_AND_FREE(out->linked_attributes, out);
+
+       for (i=0; i < linked_attributes_count; i++) {
+               const struct drsuapi_DsReplicaLinkedAttribute *ra = &linked_attributes[i];
+               struct drsuapi_DsReplicaLinkedAttribute *la = &out->linked_attributes[i];
+
+               if (ra->identifier == NULL) {
+                       talloc_free(out);
+                       return WERR_BAD_NET_RESP;
+               }
+
+               *la = *ra;
+
+               la->identifier = talloc_zero(out->linked_attributes,
+                                            struct drsuapi_DsReplicaObjectIdentifier);
+               W_ERROR_HAVE_NO_MEMORY_AND_FREE(la->identifier, out);
+
+               /*
+                * We typically only get the guid filled
+                * and the repl_meta_data module only cares abouf
+                * the guid.
+                */
+               la->identifier->guid = ra->identifier->guid;
+
+               if (ra->value.blob != NULL) {
+                       la->value.blob = talloc_zero(out->linked_attributes,
+                                                    DATA_BLOB);
+                       W_ERROR_HAVE_NO_MEMORY_AND_FREE(la->value.blob, out);
+
+                       if (ra->value.blob->length != 0) {
+                               *la->value.blob = data_blob_dup_talloc(la->value.blob,
+                                                                      *ra->value.blob);
+                               W_ERROR_HAVE_NO_MEMORY_AND_FREE(la->value.blob->data, out);
+                       }
+               }
+
+               status = dsdb_attribute_drsuapi_remote_to_local(&syntax_ctx,
+                                                               ra->attid,
+                                                               &la->attid,
+                                                               NULL);
+               if (!W_ERROR_IS_OK(status)) {
+                       DEBUG(0,(__location__": linked_attribute[%u] attid 0x%08X not found: %s\n",
+                                i, ra->attid, win_errstr(status)));
+                       return status;
+               }
+       }
+
+       out->linked_attributes_count = linked_attributes_count;
+
        /* free pfm_remote, we won't need it anymore */
        talloc_free(pfm_remote);
 
@@ -741,17 +841,16 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        TALLOC_CTX *tmp_ctx = talloc_new(objects);
        if (!tmp_ctx) {
                DEBUG(0,("Failed to start talloc\n"));
-               return WERR_NOMEM;
+               return WERR_NOT_ENOUGH_MEMORY;
        }
 
-       /* TODO: handle linked attributes */
-
        /* wrap the extended operation in a transaction 
           See [MS-DRSR] 3.3.2 Transactions
         */
        ret = ldb_transaction_start(ldb);
        if (ret != LDB_SUCCESS) {
-               DEBUG(0,(__location__ " Failed to start transaction\n"));
+               DEBUG(0,(__location__ " Failed to start transaction: %s\n",
+                        ldb_errstring(ldb)));
                return WERR_FOOBAR;
        }
 
@@ -774,7 +873,7 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                cur_schema = dsdb_get_schema(ldb, tmp_ctx);
                used_global_schema = dsdb_uses_global_schema(ldb);
 
-               ret = dsdb_reference_schema(ldb, working_schema, false);
+               ret = dsdb_reference_schema(ldb, working_schema, SCHEMA_MEMORY_ONLY);
                if (ret != LDB_SUCCESS) {
                        DEBUG(0,(__location__ "Failed to reference working schema - %s\n",
                                 ldb_strerror(ret)));
@@ -791,15 +890,18 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                if (used_global_schema) { 
                        dsdb_set_global_schema(ldb);
                } else if (cur_schema) {
-                       dsdb_reference_schema(ldb, cur_schema, false);
+                       dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
                }
 
-               if (!W_ERROR_EQUAL(objects->error, WERR_DS_DRA_MISSING_PARENT)) {
-                       DEBUG(1,("Failed to apply records: %s: %s\n",
-                                ldb_errstring(ldb), ldb_strerror(ret)));
-               } else {
+               if (W_ERROR_EQUAL(objects->error, WERR_DS_DRA_RECYCLED_TARGET)) {
+                       DEBUG(3,("Missing target while attempting to apply records: %s\n",
+                                ldb_errstring(ldb)));
+               } else if (W_ERROR_EQUAL(objects->error, WERR_DS_DRA_MISSING_PARENT)) {
                        DEBUG(3,("Missing parent while attempting to apply records: %s\n",
                                 ldb_errstring(ldb)));
+               } else {
+                       DEBUG(1,("Failed to apply records: %s: %s\n",
+                                ldb_errstring(ldb), ldb_strerror(ret)));
                }
                ldb_transaction_cancel(ldb);
                TALLOC_FREE(tmp_ctx);
@@ -821,10 +923,11 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                        if (used_global_schema) { 
                                dsdb_set_global_schema(ldb);
                        } else if (cur_schema ) {
-                               dsdb_reference_schema(ldb, cur_schema, false);
+                               dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
                        }
                        DEBUG(0,("Failed to save updated prefixMap: %s\n",
                                 win_errstr(werr)));
+                       ldb_transaction_cancel(ldb);
                        TALLOC_FREE(tmp_ctx);
                        return werr;
                }
@@ -836,10 +939,11 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                if (used_global_schema) { 
                        dsdb_set_global_schema(ldb);
                } else if (cur_schema ) {
-                       dsdb_reference_schema(ldb, cur_schema, false);
+                       dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
                }
-               DEBUG(0,(__location__ " Failed to prepare commit of transaction: %s\n",
-                        ldb_errstring(ldb)));
+               DBG_ERR(" Failed to prepare commit of transaction: %s (%s)\n",
+                       ldb_errstring(ldb),
+                       ldb_strerror(ret));
                TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;
        }
@@ -850,7 +954,7 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                if (used_global_schema) { 
                        dsdb_set_global_schema(ldb);
                } else if (cur_schema ) {
-                       dsdb_reference_schema(ldb, cur_schema, false);
+                       dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
                }
                DEBUG(0,(__location__ " Failed to load partition uSN\n"));
                ldb_transaction_cancel(ldb);
@@ -864,17 +968,29 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                if (used_global_schema) { 
                        dsdb_set_global_schema(ldb);
                } else if (cur_schema ) {
-                       dsdb_reference_schema(ldb, cur_schema, false);
+                       dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
                }
                DEBUG(0,(__location__ " Failed to commit transaction\n"));
                TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;
        }
 
-       /* if this replication partner didn't need to be notified
-          before this transaction then it still doesn't need to be
-          notified, as the changes came from this server */
-       if (seq_num2 > seq_num1 && seq_num1 <= *notify_uSN) {
+       if (seq_num1 > *notify_uSN) {
+               /*
+                * A notify was already required before
+                * the current transaction.
+                */
+       } else if (objects->originating_updates) {
+               /*
+                * Applying the replicated changes
+                * required originating updates,
+                * so a notify is required.
+                */
+       } else {
+               /*
+                * There's no need to notify the
+                * server about the change we just from it.
+                */
                *notify_uSN = seq_num2;
        }
 
@@ -891,10 +1007,13 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                 * unable to operate for other users from this
                 * point... */
                if (new_schema == NULL || new_schema == working_schema) {
-                       DEBUG(0,("Failed to re-load schema after commit of transaction (working: %p/%llu, new: %p/%llu)\n",
-                                new_schema, (unsigned long long)new_schema->metadata_usn,
-                                working_schema, (unsigned long long)working_schema->metadata_usn));
-                       dsdb_reference_schema(ldb, cur_schema, false);
+                       DBG_ERR("Failed to re-load schema after commit of "
+                               "transaction (working: %p/%"PRIu64", new: "
+                               "%p/%"PRIu64")\n", new_schema,
+                               new_schema != NULL ?
+                               new_schema->metadata_usn : 0,
+                               working_schema, working_schema->metadata_usn);
+                       dsdb_reference_schema(ldb, cur_schema, SCHEMA_MEMORY_ONLY);
                        if (used_global_schema) {
                                dsdb_set_global_schema(ldb);
                        }
@@ -998,7 +1117,7 @@ WERROR dsdb_origin_objects_commit(struct ldb_context *ldb,
        objects = talloc_array(mem_ctx, struct ldb_message *,
                               num_objects);
        if (objects == NULL) {
-               status = WERR_NOMEM;
+               status = WERR_NOT_ENOUGH_MEMORY;
                goto cancel;
        }
 
@@ -1019,7 +1138,7 @@ WERROR dsdb_origin_objects_commit(struct ldb_context *ldb,
                           struct drsuapi_DsReplicaObjectIdentifier2,
                           num_objects);
        if (ids == NULL) {
-               status = WERR_NOMEM;
+               status = WERR_NOT_ENOUGH_MEMORY;
                goto cancel;
        }