dsdb: Add log when ignoring a replicated object outside of partition
[samba.git] / source4 / dsdb / repl / replicated_objects.c
index df880ad737359c2ebed06503251ecdfeb5779c98..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,16 +131,17 @@ 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;
-               DLIST_ADD_END(schema_list, schema_list_item, struct schema_list);
+               DLIST_ADD_END(schema_list, schema_list_item);
        }
 
        /* resolve objects until all are resolved and in local schema */
@@ -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;
                        }
                }
@@ -188,6 +192,7 @@ WERROR dsdb_repl_resolve_working_schema(struct ldb_context *ldb,
                         * an object. We should convert more objects on next pass.
                         */
                        werr = dsdb_convert_object_ex(ldb, working_schema,
+                                                     NULL,
                                                      pfm_remote,
                                                      cur, &empty_key,
                                                      ignore_attids,
@@ -241,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;
                }
 
@@ -256,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;
 }
 
@@ -286,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,
@@ -314,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;
@@ -338,6 +386,7 @@ static bool dsdb_attid_in_list(const uint32_t attid_list[], uint32_t attid)
 
 WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                              const struct dsdb_schema *schema,
+                             struct ldb_dn *partition_dn,
                              const struct dsdb_schema_prefixmap *pfm_remote,
                              const struct drsuapi_DsReplicaObjectListItemEx *in,
                              const DATA_BLOB *gensec_skey,
@@ -346,25 +395,18 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                              TALLOC_CTX *mem_ctx,
                              struct dsdb_extended_replicated_object *out)
 {
-       NTSTATUS nt_status;
-       WERROR status;
+       WERROR status = WERR_OK;
        uint32_t i;
        struct ldb_message *msg;
        struct replPropertyMetaDataBlob *md;
        int instanceType;
        struct ldb_message_element *instanceType_e = NULL;
-       struct ldb_val guid_value;
-       struct ldb_val parent_guid_value;
        NTTIME whenChanged = 0;
        time_t whenChanged_t;
        const char *whenChanged_s;
-       struct drsuapi_DsReplicaAttribute *name_a = NULL;
-       struct drsuapi_DsReplicaMetaData *name_d = NULL;
-       struct replPropertyMetaData1 *rdn_m = NULL;
        struct dom_sid *sid = NULL;
        uint32_t rid = 0;
        uint32_t attr_count;
-       int ret;
 
        if (!in->object.identifier) {
                return WERR_FOOBAR;
@@ -395,7 +437,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
        msg->num_elements       = in->object.attribute_ctr.num_attributes;
        msg->elements           = talloc_array(msg, struct ldb_message_element,
-                                              msg->num_elements + 1); /* +1 because of the RDN attribute */
+                                              msg->num_elements);
        W_ERROR_HAVE_NO_MEMORY(msg->elements);
 
        md = talloc(mem_ctx, struct replPropertyMetaDataBlob);
@@ -407,7 +449,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
        md->ctr.ctr1.reserved   = 0;
        md->ctr.ctr1.array      = talloc_array(mem_ctx,
                                               struct replPropertyMetaData1,
-                                              md->ctr.ctr1.count + 1); /* +1 because of the RDN attribute */
+                                              md->ctr.ctr1.count);
        W_ERROR_HAVE_NO_MEMORY(md->ctr.ctr1.array);
 
        for (i=0, attr_count=0; i < in->meta_data_ctr->count; i++, attr_count++) {
@@ -444,93 +486,94 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                }
 
                for (j=0; j<a->value_ctr.num_values; j++) {
-                       status = drsuapi_decrypt_attribute(a->value_ctr.values[j].blob, gensec_skey, rid, a);
-                       W_ERROR_NOT_OK_RETURN(status);
+                       status = drsuapi_decrypt_attribute(a->value_ctr.values[j].blob,
+                                                          gensec_skey, rid,
+                                                          dsdb_repl_flags, a);
+                       if (!W_ERROR_IS_OK(status)) {
+                               break;
+                       }
+               }
+               if (W_ERROR_EQUAL(status, WERR_TOO_MANY_SECRETS)) {
+                       WERROR get_name_status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, pfm_remote,
+                                                                              a, msg->elements, e, NULL);
+                       if (W_ERROR_IS_OK(get_name_status)) {
+                               DEBUG(0, ("Unxpectedly got secret value %s on %s from DRS server\n",
+                                         e->name, ldb_dn_get_linearized(msg->dn)));
+                       } else {
+                               DEBUG(0, ("Unxpectedly got secret value on %s from DRS server",
+                                         ldb_dn_get_linearized(msg->dn)));
+                       }
+               } else if (!W_ERROR_IS_OK(status)) {
+                       return status;
                }
 
+               /*
+                * This function also fills in the local attid value,
+                * based on comparing the remote and local prefixMap
+                * tables.  If we don't convert the value, then we can
+                * have invalid values in the replPropertyMetaData we
+                * store on disk, as the prefixMap is per host, not
+                * per-domain.  This may be why Microsoft added the
+                * msDS-IntID feature, however this is not used for
+                * extra attributes in the schema partition itself.
+                */
                status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, pfm_remote,
-                                                      a, msg->elements, e);
+                                                      a, msg->elements, e,
+                                                      &m->attid);
                W_ERROR_NOT_OK_RETURN(status);
 
-               m->attid                        = a->attid;
                m->version                      = d->version;
                m->originating_change_time      = d->originating_change_time;
                m->originating_invocation_id    = d->originating_invocation_id;
                m->originating_usn              = d->originating_usn;
                m->local_usn                    = 0;
 
-               if (d->originating_change_time > whenChanged) {
-                       whenChanged = d->originating_change_time;
-               }
-
                if (a->attid == DRSUAPI_ATTID_name) {
-                       name_a = a;
-                       name_d = d;
-               }
-       }
-
-       msg->num_elements = attr_count;
-       md->ctr.ctr1.count = attr_count;
-       if (name_a) {
-               rdn_m = &md->ctr.ctr1.array[md->ctr.ctr1.count];
-       }
-
-       if (rdn_m) {
-               struct ldb_message_element *el;
-               const char *rdn_name = NULL;
-               const struct ldb_val *rdn_value = NULL;
-               const struct dsdb_attribute *rdn_attr = NULL;
-               uint32_t rdn_attid;
-
-               /*
-                * We only need the schema calls for the RDN in this
-                * codepath, and by doing this we avoid needing to
-                * have the dsdb_attribute_by_lDAPDisplayName accessor
-                * working during the schema load.
-                */
-               rdn_name        = ldb_dn_get_rdn_name(msg->dn);
-               rdn_attr        = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
-               if (!rdn_attr) {
-                       return WERR_FOOBAR;
-               }
-               rdn_attid       = rdn_attr->attributeID_id;
-               rdn_value       = ldb_dn_get_rdn_val(msg->dn);
-
-               el = ldb_msg_find_element(msg, rdn_attr->lDAPDisplayName);
-               if (!el) {
-                       ret = ldb_msg_add_value(msg, rdn_attr->lDAPDisplayName, rdn_value, NULL);
-                       if (ret != LDB_SUCCESS) {
+                       const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
+                       if (rdn_val == NULL) {
+                               DEBUG(0, ("Unxpectedly unable to get RDN from %s for validation",
+                                         ldb_dn_get_linearized(msg->dn)));
                                return WERR_FOOBAR;
                        }
-               } else {
-                       if (el->num_values != 1) {
-                               DEBUG(0,(__location__ ": Unexpected num_values=%u\n",
-                                        el->num_values));
-                               return WERR_FOOBAR;                             
+                       if (e->num_values != 1) {
+                               DEBUG(0, ("Unxpectedly got wrong number of attribute values (got %u, expected 1) when checking RDN against name of %s",
+                                         e->num_values,
+                                         ldb_dn_get_linearized(msg->dn)));
+                               return WERR_FOOBAR;
                        }
-                       if (!ldb_val_equal_exact(&el->values[0], rdn_value)) {
-                               DEBUG(0,(__location__ ": RDN value changed? '%*.*s' '%*.*s'\n",
-                                        (int)el->values[0].length, (int)el->values[0].length, el->values[0].data,
-                                        (int)rdn_value->length, (int)rdn_value->length, rdn_value->data));
-                               return WERR_FOOBAR;                             
+                       if (data_blob_cmp(rdn_val,
+                                         &e->values[0]) != 0) {
+                               DEBUG(0, ("Unxpectedly got mismatching RDN values when checking RDN against name of %s",
+                                         ldb_dn_get_linearized(msg->dn)));
+                               return WERR_FOOBAR;
                        }
                }
-
-               rdn_m->attid                            = rdn_attid;
-               rdn_m->version                          = name_d->version;
-               rdn_m->originating_change_time          = name_d->originating_change_time;
-               rdn_m->originating_invocation_id        = name_d->originating_invocation_id;
-               rdn_m->originating_usn                  = name_d->originating_usn;
-               rdn_m->local_usn                        = 0;
-               md->ctr.ctr1.count++;
+               if (d->originating_change_time > whenChanged) {
+                       whenChanged = d->originating_change_time;
+               }
 
        }
 
+       msg->num_elements = attr_count;
+       md->ctr.ctr1.count = attr_count;
+
        if (instanceType_e == NULL) {
                return WERR_FOOBAR;
        }
 
        instanceType = ldb_msg_find_attr_as_int(msg, "instanceType", 0);
+
+       if ((instanceType & INSTANCE_TYPE_IS_NC_HEAD)
+           && partition_dn != NULL) {
+               int partition_dn_cmp = ldb_dn_compare(partition_dn, msg->dn);
+               if (partition_dn_cmp != 0) {
+                       DEBUG(4, ("Remote server advised us of a new partition %s while processing %s, ignoring\n",
+                                 ldb_dn_get_linearized(msg->dn),
+                                 ldb_dn_get_linearized(partition_dn)));
+                       return WERR_DS_ADD_REPLICA_INHIBITED;
+               }
+       }
+
        if (dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
                /* the instanceType type for partial_replica
                   replication is sent via DRS with TYPE_WRITE set, but
@@ -574,23 +617,17 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
        whenChanged_s = ldb_timestring(msg, whenChanged_t);
        W_ERROR_HAVE_NO_MEMORY(whenChanged_s);
 
-       nt_status = GUID_to_ndr_blob(&in->object.identifier->guid, msg, &guid_value);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               return ntstatus_to_werror(nt_status);
-       }
+       out->object_guid = in->object.identifier->guid;
 
-       if (in->parent_object_guid) {
-               nt_status = GUID_to_ndr_blob(in->parent_object_guid, msg, &parent_guid_value);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       return ntstatus_to_werror(nt_status);
-               }
+       if (in->parent_object_guid == NULL) {
+               out->parent_guid = NULL;
        } else {
-               parent_guid_value = data_blob_null;
+               out->parent_guid = talloc(mem_ctx, struct GUID);
+               W_ERROR_HAVE_NO_MEMORY(out->parent_guid);
+               *out->parent_guid = *in->parent_object_guid;
        }
 
        out->msg                = msg;
-       out->guid_value         = guid_value;
-       out->parent_guid_value  = parent_guid_value;
        out->when_changed       = whenChanged_s;
        out->meta_data          = md;
        return WERR_OK;
@@ -598,7 +635,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
 WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                                       const struct dsdb_schema *schema,
-                                      const char *partition_dn_str,
+                                      struct ldb_dn *partition_dn,
                                       const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr,
                                       uint32_t object_count,
                                       const struct drsuapi_DsReplicaObjectListItemEx *first_object,
@@ -612,10 +649,10 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                                       struct dsdb_extended_replicated_objects **objects)
 {
        WERROR status;
-       struct ldb_dn *partition_dn;
        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);
@@ -630,18 +667,19 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        schema = talloc_reference(out, schema);
        W_ERROR_HAVE_NO_MEMORY(schema);
 
-       partition_dn = ldb_dn_new(out, ldb, partition_dn_str);
-       W_ERROR_HAVE_NO_MEMORY_AND_FREE(partition_dn, out);
-
        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
@@ -649,8 +687,8 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                 */
                status = dsdb_schema_info_cmp(schema, mapping_ctr);
                if (!W_ERROR_IS_OK(status)) {
-                       DEBUG(1,("Remote schema has changed while replicating %s\n",
-                                partition_dn_str));
+                       DEBUG(4,("Can't replicate %s because remote schema has changed since we last replicated the schema\n",
+                                ldb_dn_get_linearized(partition_dn)));
                        talloc_free(out);
                        return status;
                }
@@ -661,28 +699,43 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        out->source_dsa         = source_dsa;
        out->uptodateness_vector= uptodateness_vector;
 
-       out->num_objects        = object_count;
+       out->num_objects        = 0;
        out->objects            = talloc_array(out,
                                               struct dsdb_extended_replicated_object,
-                                              out->num_objects);
+                                              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 == out->num_objects) {
+               if (i == object_count) {
                        talloc_free(out);
                        return WERR_FOOBAR;
                }
 
-               status = dsdb_convert_object_ex(ldb, schema, pfm_remote,
+               status = dsdb_convert_object_ex(ldb, schema, out->partition_dn,
+                                               pfm_remote,
                                                cur, gensec_skey,
                                                NULL,
                                                dsdb_repl_flags,
-                                               out->objects, &out->objects[i]);
+                                               out->objects,
+                                               &out->objects[out->num_objects]);
+
+               /*
+                * Check to see if we have been advised of a
+                * subdomain or new application partition.  We don't
+                * want to start on that here, instead the caller
+                * should consider if it would like to replicate it
+                * 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;
+               }
+
                if (!W_ERROR_IS_OK(status)) {
                        talloc_free(out);
                        DEBUG(0,("Failed to convert object %s: %s\n",
@@ -690,11 +743,73 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                                 win_errstr(status)));
                        return status;
                }
+
+               /* Assuming we didn't skip or error, increment the number of objects */
+               out->num_objects++;
        }
-       if (i != out->num_objects) {
+       out->objects = talloc_realloc(out, out->objects,
+                                     struct dsdb_extended_replicated_object,
+                                     out->num_objects);
+       if (out->num_objects != 0 && out->objects == NULL) {
                talloc_free(out);
                return WERR_FOOBAR;
        }
+       if (i != object_count) {
+               talloc_free(out);
+               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);
@@ -726,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;
        }
 
@@ -759,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)));
@@ -776,13 +890,25 @@ 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 apply records: %s: %s\n",
-                        ldb_errstring(ldb), ldb_strerror(ret)));
+               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);
+
+               if (!W_ERROR_IS_OK(objects->error)) {
+                       return objects->error;
+               }
                return WERR_FOOBAR;
        }
        talloc_free(ext_res);
@@ -797,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;
                }
@@ -812,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;
        }
@@ -826,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);
@@ -840,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;
        }
 
@@ -859,83 +999,28 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
         * a schema cache being refreshed from database.
         */
        if (working_schema) {
-               struct ldb_message *msg;
-               struct ldb_request *req;
-
-               /* Force a reload */
-               working_schema->last_refresh = 0;
+               /* Reload the schema */
                new_schema = dsdb_get_schema(ldb, tmp_ctx);
-               /* TODO: 
+               /* TODO:
                 * If dsdb_get_schema() fails, we just fall back
                 * to what we had.  However, the database is probably
                 * unable to operate for other users from this
                 * point... */
-               if (new_schema && used_global_schema) {
-                       dsdb_make_schema_global(ldb, new_schema);
-               } else if (used_global_schema) { 
-                       DEBUG(0,("Failed to re-load schema after commit of transaction\n"));
-                       dsdb_set_global_schema(ldb);
-                       TALLOC_FREE(tmp_ctx);
-                       return WERR_INTERNAL_ERROR;
-               } else {
-                       DEBUG(0,("Failed to re-load schema after commit of transaction\n"));
-                       dsdb_reference_schema(ldb, cur_schema, false);
-                       TALLOC_FREE(tmp_ctx);
-                       return WERR_INTERNAL_ERROR;
-               }
-               msg = ldb_msg_new(tmp_ctx);
-               if (msg == NULL) {
-                       TALLOC_FREE(tmp_ctx);
-                       return WERR_NOMEM;
-               }
-               msg->dn = ldb_dn_new(msg, ldb, "");
-               if (msg->dn == NULL) {
-                       TALLOC_FREE(tmp_ctx);
-                       return WERR_NOMEM;
-               }
-
-               ret = ldb_msg_add_string(msg, "schemaUpdateNow", "1");
-               if (ret != LDB_SUCCESS) {
+               if (new_schema == NULL || new_schema == working_schema) {
+                       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);
+                       }
                        TALLOC_FREE(tmp_ctx);
                        return WERR_INTERNAL_ERROR;
-               }
-
-               ret = ldb_build_mod_req(&req, ldb, objects,
-                               msg,
-                               NULL,
-                               NULL,
-                               ldb_op_default_callback,
-                               NULL);
-
-               if (ret != LDB_SUCCESS) {
-                       TALLOC_FREE(tmp_ctx);
-                       return WERR_DS_DRA_INTERNAL_ERROR;
-               }
-
-               ret = ldb_transaction_start(ldb);
-               if (ret != LDB_SUCCESS) {
-                       TALLOC_FREE(tmp_ctx);
-                       DEBUG(0, ("Autotransaction start failed\n"));
-                       return WERR_DS_DRA_INTERNAL_ERROR;
-               }
-
-               ret = ldb_request(ldb, req);
-               if (ret == LDB_SUCCESS) {
-                       ret = ldb_wait(req->handle, LDB_WAIT_ALL);
-               }
-
-               if (ret == LDB_SUCCESS) {
-                       ret = ldb_transaction_commit(ldb);
-               } else {
-                       DEBUG(0, ("Schema update now failed: %s\n",
-                                 ldb_errstring(ldb)));
-                       ldb_transaction_cancel(ldb);
-               }
-
-               if (ret != LDB_SUCCESS) {
-                       DEBUG(0, ("Commit failed: %s\n", ldb_errstring(ldb)));
-                       TALLOC_FREE(tmp_ctx);
-                       return WERR_DS_INTERNAL_FAILURE;
+               } else if (used_global_schema) {
+                       dsdb_make_schema_global(ldb, new_schema);
                }
        }
 
@@ -984,7 +1069,7 @@ static WERROR dsdb_origin_object_convert(struct ldb_context *ldb,
                e = &msg->elements[i];
 
                status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, schema->prefixmap,
-                                                      a, msg->elements, e);
+                                                      a, msg->elements, e, NULL);
                W_ERROR_NOT_OK_RETURN(status);
        }
 
@@ -1032,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;
        }
 
@@ -1053,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;
        }