dsdb-repl: Allow the name attribute (and name-based schema lookups) to be skipped...
[obnox/samba/samba-obnox.git] / source4 / dsdb / repl / replicated_objects.c
index 9f9be3c7781114b18141e3bbf3a259bd676719c8..29b494b8164e34664bc4dabd9c92961100edd89e 100644 (file)
@@ -61,6 +61,14 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
        struct dsdb_schema *working_schema;
        const struct drsuapi_DsReplicaObjectListItemEx *cur;
        int ret, pass_no;
+       uint32_t ignore_attids[] = {
+                       DRSUAPI_ATTID_auxiliaryClass,
+                       DRSUAPI_ATTID_mayContain,
+                       DRSUAPI_ATTID_mustContain,
+                       DRSUAPI_ATTID_possSuperiors,
+                       DRSUAPI_ATTID_systemPossSuperiors,
+                       DRSUAPI_ATTID_INVALID
+       };
 
        /* 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);
@@ -111,10 +119,12 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
                         */
                        werr = dsdb_convert_object_ex(ldb, working_schema, pfm_remote,
                                                      cur, gensec_skey,
+                                                     ignore_attids,
+                                                     0,
                                                      tmp_ctx, &object);
                        if (!W_ERROR_IS_OK(werr)) {
-                               DEBUG(1,("Warning: Failed to convert schema object %s into ldb msg\n",
-                                        cur->object.identifier->dn));
+                               DEBUG(4,("debug: Failed to convert schema object %s into ldb msg, will try during next loop\n",
+                                         cur->object.identifier->dn));
 
                                failed_obj_count++;
                        } else {
@@ -127,7 +137,7 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
                                                                       working_schema,
                                                                       object.msg);
                                if (!W_ERROR_IS_OK(werr)) {
-                                       DEBUG(1,("Warning: failed to convert object %s into a schema element: %s\n",
+                                       DEBUG(4,("debug: failed to convert object %s into a schema element, will try during next loop: %s\n",
                                                 ldb_dn_get_linearized(object.msg->dn),
                                                 win_errstr(werr)));
                                        failed_obj_count++;
@@ -140,8 +150,8 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
                }
                talloc_free(tmp_ctx);
 
-               DEBUG(4,("Schema load pass %d: %d/%d of %d objects left to be converted.\n",
-                        pass_no, failed_obj_count, converted_obj_count, object_count));
+               DEBUG(4,("Schema load pass %d: converted %d, %d of %d objects left to be converted.\n",
+                        pass_no, converted_obj_count, failed_obj_count, object_count));
                pass_no++;
 
                /* check if we converted any objects in this pass */
@@ -183,6 +193,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                              const struct drsuapi_DsReplicaObjectListItemEx *in,
                              const DATA_BLOB *gensec_skey,
                              const uint32_t *ignore_attids,
+                             uint32_t dsdb_repl_flags,
                              TALLOC_CTX *mem_ctx,
                              struct dsdb_extended_replicated_object *out)
 {
@@ -191,14 +202,13 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
        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;
-       const char *rdn_name = NULL;
-       const struct ldb_val *rdn_value = NULL;
-       const struct dsdb_attribute *rdn_attr = NULL;
-       uint32_t rdn_attid;
        struct drsuapi_DsReplicaAttribute *name_a = NULL;
        struct drsuapi_DsReplicaMetaData *name_d = NULL;
        struct replPropertyMetaData1 *rdn_m = NULL;
@@ -234,17 +244,9 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
        msg->dn                 = ldb_dn_new(msg, ldb, in->object.identifier->dn);
        W_ERROR_HAVE_NO_MEMORY(msg->dn);
 
-       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);
-
        msg->num_elements       = in->object.attribute_ctr.num_attributes;
        msg->elements           = talloc_array(msg, struct ldb_message_element,
-                                              msg->num_elements);
+                                              msg->num_elements + 1); /* +1 because of the RDN attribute */
        W_ERROR_HAVE_NO_MEMORY(msg->elements);
 
        md = talloc(mem_ctx, struct replPropertyMetaDataBlob);
@@ -276,6 +278,13 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                        continue;
                }
 
+               if (a->attid == DRSUAPI_ATTID_instanceType) {
+                       if (instanceType_e != NULL) {
+                               return WERR_FOOBAR;
+                       }
+                       instanceType_e = e;
+               }
+
                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);
@@ -310,6 +319,25 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
        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);
@@ -340,6 +368,50 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
        }
 
+       if (instanceType_e == NULL) {
+               return WERR_FOOBAR;
+       }
+
+       instanceType = ldb_msg_find_attr_as_int(msg, "instanceType", 0);
+       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
+                  must be used on the client with TYPE_WRITE removed
+               */
+               if (instanceType & INSTANCE_TYPE_WRITE) {
+                       /*
+                        * Make sure we do not change the order
+                        * of msg->elements!
+                        *
+                        * That's why we use
+                        * instanceType_e->num_values = 0
+                        * instead of
+                        * ldb_msg_remove_attr(msg, "instanceType");
+                        */
+                       struct ldb_message_element *e;
+
+                       e = ldb_msg_find_element(msg, "instanceType");
+                       if (e != instanceType_e) {
+                               DEBUG(0,("instanceType_e[%p] changed to e[%p]\n",
+                                        instanceType_e, e));
+                               return WERR_FOOBAR;
+                       }
+
+                       instanceType_e->num_values = 0;
+
+                       instanceType &= ~INSTANCE_TYPE_WRITE;
+                       if (ldb_msg_add_fmt(msg, "instanceType", "%d", instanceType) != LDB_SUCCESS) {
+                               return WERR_INTERNAL_ERROR;
+                       }
+               }
+       } else {
+               if (!(instanceType & INSTANCE_TYPE_WRITE)) {
+                       DEBUG(0, ("Refusing to replicate %s from a read-only repilca into a read-write replica!\n",
+                                 ldb_dn_get_linearized(msg->dn)));
+                       return WERR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA;
+               }
+       }
+
        whenChanged_t = nt_time_to_unix(whenChanged);
        whenChanged_s = ldb_timestring(msg, whenChanged_t);
        W_ERROR_HAVE_NO_MEMORY(whenChanged_s);
@@ -349,8 +421,18 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                return ntstatus_to_werror(nt_status);
        }
 
+       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);
+               }
+       } else {
+               parent_guid_value = data_blob_null;
+       }
+
        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;
@@ -367,6 +449,7 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                                       const struct repsFromTo1 *source_dsa,
                                       const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector,
                                       const DATA_BLOB *gensec_skey,
+                                      uint32_t dsdb_repl_flags,
                                       TALLOC_CTX *mem_ctx,
                                       struct dsdb_extended_replicated_objects **objects)
 {
@@ -380,6 +463,7 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        out = talloc_zero(mem_ctx, struct dsdb_extended_replicated_objects);
        W_ERROR_HAVE_NO_MEMORY(out);
        out->version            = DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION;
+       out->dsdb_repl_flags    = dsdb_repl_flags;
 
        /*
         * Ensure schema is kept valid for as long as 'out'
@@ -439,6 +523,7 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
                status = dsdb_convert_object_ex(ldb, schema, pfm_remote,
                                                cur, gensec_skey,
                                                NULL,
+                                               dsdb_repl_flags,
                                                out->objects, &out->objects[i]);
                if (!W_ERROR_IS_OK(status)) {
                        talloc_free(out);
@@ -475,8 +560,16 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        WERROR werr;
        struct ldb_result *ext_res;
        struct dsdb_schema *cur_schema = NULL;
+       struct dsdb_schema *new_schema = NULL;
        int ret;
        uint64_t seq_num1, seq_num2;
+       bool used_global_schema = false;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(objects);
+       if (!tmp_ctx) {
+               DEBUG(0,("Failed to start talloc\n"));
+               return WERR_NOMEM;
+       }
 
        /* TODO: handle linked attributes */
 
@@ -493,6 +586,7 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        if (ret != LDB_SUCCESS) {
                DEBUG(0,(__location__ " Failed to load partition uSN\n"));
                ldb_transaction_cancel(ldb);
+               TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;             
        }
 
@@ -504,7 +598,8 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
         */
        if (working_schema) {
                /* store current schema so we can fall back in case of failure */
-               cur_schema = dsdb_get_schema(ldb, working_schema);
+               cur_schema = dsdb_get_schema(ldb, tmp_ctx);
+               used_global_schema = dsdb_uses_global_schema(ldb);
 
                ret = dsdb_reference_schema(ldb, working_schema, false);
                if (ret != LDB_SUCCESS) {
@@ -512,6 +607,7 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                                 ldb_strerror(ret)));
                        /* TODO: Map LDB Error to NTSTATUS? */
                        ldb_transaction_cancel(ldb);
+                       TALLOC_FREE(tmp_ctx);
                        return WERR_INTERNAL_ERROR;
                }
        }
@@ -519,14 +615,16 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        ret = ldb_extended(ldb, DSDB_EXTENDED_REPLICATED_OBJECTS_OID, objects, &ext_res);
        if (ret != LDB_SUCCESS) {
                /* restore previous schema */
-               if (cur_schema ) {
+               if (used_global_schema) { 
+                       dsdb_set_global_schema(ldb);
+               } else if (cur_schema) {
                        dsdb_reference_schema(ldb, cur_schema, false);
-                       dsdb_make_schema_global(ldb, cur_schema);
                }
 
                DEBUG(0,("Failed to apply records: %s: %s\n",
                         ldb_errstring(ldb), ldb_strerror(ret)));
                ldb_transaction_cancel(ldb);
+               TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;
        }
        talloc_free(ext_res);
@@ -538,12 +636,14 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                                                              working_schema);
                if (!W_ERROR_IS_OK(werr)) {
                        /* restore previous schema */
-                       if (cur_schema ) {
+                       if (used_global_schema) { 
+                               dsdb_set_global_schema(ldb);
+                       } else if (cur_schema ) {
                                dsdb_reference_schema(ldb, cur_schema, false);
-                               dsdb_make_schema_global(ldb, cur_schema);
                        }
                        DEBUG(0,("Failed to save updated prefixMap: %s\n",
                                 win_errstr(werr)));
+                       TALLOC_FREE(tmp_ctx);
                        return werr;
                }
        }
@@ -551,24 +651,28 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        ret = ldb_transaction_prepare_commit(ldb);
        if (ret != LDB_SUCCESS) {
                /* restore previous schema */
-               if (cur_schema ) {
+               if (used_global_schema) { 
+                       dsdb_set_global_schema(ldb);
+               } else if (cur_schema ) {
                        dsdb_reference_schema(ldb, cur_schema, false);
-                       dsdb_make_schema_global(ldb, cur_schema);
                }
                DEBUG(0,(__location__ " Failed to prepare commit of transaction: %s\n",
                         ldb_errstring(ldb)));
+               TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;
        }
 
        ret = dsdb_load_partition_usn(ldb, objects->partition_dn, &seq_num2, NULL);
        if (ret != LDB_SUCCESS) {
                /* restore previous schema */
-               if (cur_schema ) {
+               if (used_global_schema) { 
+                       dsdb_set_global_schema(ldb);
+               } else if (cur_schema ) {
                        dsdb_reference_schema(ldb, cur_schema, false);
-                       dsdb_make_schema_global(ldb, cur_schema);
                }
                DEBUG(0,(__location__ " Failed to load partition uSN\n"));
                ldb_transaction_cancel(ldb);
+               TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;             
        }
 
@@ -582,11 +686,13 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        ret = ldb_transaction_commit(ldb);
        if (ret != LDB_SUCCESS) {
                /* restore previous schema */
-               if (cur_schema ) {
+               if (used_global_schema) { 
+                       dsdb_set_global_schema(ldb);
+               } else if (cur_schema ) {
                        dsdb_reference_schema(ldb, cur_schema, false);
-                       dsdb_make_schema_global(ldb, cur_schema);
                }
                DEBUG(0,(__location__ " Failed to commit transaction\n"));
+               TALLOC_FREE(tmp_ctx);
                return WERR_FOOBAR;
        }
 
@@ -595,11 +701,83 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
         * a schema cache being refreshed from database.
         */
        if (working_schema) {
-               cur_schema = dsdb_get_schema(ldb, NULL);
-               /* TODO: What we do in case dsdb_get_schema() fail?
-                *       We can't fallback at this point anymore */
-               if (cur_schema) {
-                       dsdb_make_schema_global(ldb, cur_schema);
+               struct ldb_message *msg;
+               struct ldb_request *req;
+
+               /* Force a reload */
+               working_schema->last_refresh = 0;
+               new_schema = dsdb_get_schema(ldb, tmp_ctx);
+               /* 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) {
+                       TALLOC_FREE(tmp_ctx);
+                       return WERR_INTERNAL_ERROR;
+               }
+
+               ret = ldb_build_mod_req(&req, ldb, objects,
+                               msg,
+                               LDB_SCOPE_BASE,
+                               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;
                }
        }
 
@@ -607,6 +785,7 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                 objects->num_objects, objects->linked_attributes_count,
                 ldb_dn_get_linearized(objects->partition_dn)));
                 
+       TALLOC_FREE(tmp_ctx);
        return WERR_OK;
 }
 
@@ -661,6 +840,7 @@ WERROR dsdb_origin_objects_commit(struct ldb_context *ldb,
                                  TALLOC_CTX *mem_ctx,
                                  const struct drsuapi_DsReplicaObjectListItem *first_object,
                                  uint32_t *_num,
+                                 uint32_t dsdb_repl_flags,
                                  struct drsuapi_DsReplicaObjectIdentifier2 **_ids)
 {
        WERROR status;
@@ -719,6 +899,32 @@ WERROR dsdb_origin_objects_commit(struct ldb_context *ldb,
                goto cancel;
        }
 
+       if (dsdb_repl_flags & DSDB_REPL_FLAG_ADD_NCNAME) {
+               /* check for possible NC creation */
+               for (i=0; i < num_objects; i++) {
+                       struct ldb_message *msg = objects[i];
+                       struct ldb_message_element *el;
+                       struct ldb_dn *nc_dn;
+
+                       if (ldb_msg_check_string_attribute(msg, "objectClass", "crossRef") == 0) {
+                               continue;
+                       }
+                       el = ldb_msg_find_element(msg, "nCName");
+                       if (el == NULL || el->num_values != 1) {
+                               continue;
+                       }
+                       nc_dn = ldb_dn_from_ldb_val(objects, ldb, &el->values[0]);
+                       if (!ldb_dn_validate(nc_dn)) {
+                               continue;
+                       }
+                       ret = dsdb_create_partial_replica_NC(ldb, nc_dn);
+                       if (ret != LDB_SUCCESS) {
+                               status = WERR_DS_INTERNAL_FAILURE;
+                               goto cancel;
+                       }
+               }
+       }
+
        for (i=0; i < num_objects; i++) {
                struct dom_sid *sid = NULL;
                struct ldb_request *add_req;