s4-dsdb: added new control DSDB_MODIFY_PARTIAL_REPLICA
[nivanova/samba-autobuild/.git] / source4 / dsdb / repl / replicated_objects.c
index 0965538570db2d651af7c9ef250e48c95fa2ffeb..5ccf05255487213c7e3a76dec7a7a9b6743a1067 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "includes.h"
 #include "dsdb/samdb/samdb.h"
-#include "lib/ldb/include/ldb_errors.h"
+#include <ldb_errors.h>
 #include "../lib/util/dlinklist.h"
 #include "librpc/gen_ndr/ndr_misc.h"
 #include "librpc/gen_ndr/ndr_drsuapi.h"
@@ -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++;
@@ -163,11 +173,27 @@ WERROR dsdb_repl_make_working_schema(struct ldb_context *ldb,
        return WERR_OK;
 }
 
+static bool dsdb_attid_in_list(const uint32_t attid_list[], uint32_t attid)
+{
+       const uint32_t *cur;
+       if (!attid_list) {
+               return false;
+       }
+       for (cur = attid_list; *cur != DRSUAPI_ATTID_INVALID; cur++) {
+               if (*cur == attid) {
+                       return true;
+               }
+       }
+       return false;
+}
+
 WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                              const struct dsdb_schema *schema,
                              const struct dsdb_schema_prefixmap *pfm_remote,
                              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)
 {
@@ -189,6 +215,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
        struct replPropertyMetaData1 *rdn_m = NULL;
        struct dom_sid *sid = NULL;
        uint32_t rid = 0;
+       uint32_t attr_count;
        int ret;
 
        if (!in->object.identifier) {
@@ -243,7 +270,7 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                                               md->ctr.ctr1.count + 1); /* +1 because of the RDN attribute */
        W_ERROR_HAVE_NO_MEMORY(md->ctr.ctr1.array);
 
-       for (i=0; i < in->meta_data_ctr->count; i++) {
+       for (i=0, attr_count=0; i < in->meta_data_ctr->count; i++, attr_count++) {
                struct drsuapi_DsReplicaAttribute *a;
                struct drsuapi_DsReplicaMetaData *d;
                struct replPropertyMetaData1 *m;
@@ -252,8 +279,13 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
                a = &in->object.attribute_ctr.attributes[i];
                d = &in->meta_data_ctr->meta_data[i];
-               m = &md->ctr.ctr1.array[i];
-               e = &msg->elements[i];
+               m = &md->ctr.ctr1.array[attr_count];
+               e = &msg->elements[attr_count];
+
+               if (dsdb_attid_in_list(ignore_attids, a->attid)) {
+                       attr_count--;
+                       continue;
+               }
 
                for (j=0; j<a->value_ctr.num_values; j++) {
                        status = drsuapi_decrypt_attribute(a->value_ctr.values[j].blob, gensec_skey, rid, a);
@@ -278,10 +310,15 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
                if (a->attid == DRSUAPI_ATTID_name) {
                        name_a = a;
                        name_d = d;
-                       rdn_m = &md->ctr.ctr1.array[md->ctr.ctr1.count];
                }
        }
 
+       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;
                el = ldb_msg_find_element(msg, rdn_attr->lDAPDisplayName);
@@ -314,6 +351,21 @@ WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
 
        }
 
+       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
+               */
+               int instanceType = ldb_msg_find_attr_as_int(msg, "instanceType", 0);
+               if (instanceType & INSTANCE_TYPE_WRITE) {
+                       instanceType &= ~INSTANCE_TYPE_WRITE;
+                       ldb_msg_remove_attr(msg, "instanceType");
+                       if (ldb_msg_add_fmt(msg, "instanceType", "%d", instanceType) != LDB_SUCCESS) {
+                               return WERR_INTERNAL_ERROR;
+                       }
+               }
+       }
+
        whenChanged_t = nt_time_to_unix(whenChanged);
        whenChanged_s = ldb_timestring(msg, whenChanged_t);
        W_ERROR_HAVE_NO_MEMORY(whenChanged_s);
@@ -341,6 +393,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)
 {
@@ -354,12 +407,14 @@ 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'
         * which may contain pointers to it
         */
-       talloc_reference(out, schema);
+       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);
@@ -411,6 +466,8 @@ 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);
@@ -432,11 +489,21 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        return WERR_OK;
 }
 
+/**
+ * Commits a list of replicated objects.
+ *
+ * @param working_schema dsdb_schema to be used for resolving
+ *                      Classes/Attributes during Schema replication. If not NULL,
+ *                      it will be set on ldb and used while committing replicated objects
+ */
 WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
+                                     struct dsdb_schema *working_schema,
                                      struct dsdb_extended_replicated_objects *objects,
                                      uint64_t *notify_uSN)
 {
+       WERROR werr;
        struct ldb_result *ext_res;
+       struct dsdb_schema *cur_schema = NULL;
        int ret;
        uint64_t seq_num1, seq_num2;
 
@@ -458,8 +525,34 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                return WERR_FOOBAR;             
        }
 
+       /*
+        * Set working_schema for ldb in case we are replicating from Schema NC.
+        * Schema won't be reloaded during Replicated Objects commit, as it is
+        * done in a transaction. So we need some way to search for newly
+        * added Classes and Attributes
+        */
+       if (working_schema) {
+               /* store current schema so we can fall back in case of failure */
+               cur_schema = dsdb_get_schema(ldb, working_schema);
+
+               ret = dsdb_reference_schema(ldb, working_schema, false);
+               if (ret != LDB_SUCCESS) {
+                       DEBUG(0,(__location__ "Failed to reference working schema - %s\n",
+                                ldb_strerror(ret)));
+                       /* TODO: Map LDB Error to NTSTATUS? */
+                       ldb_transaction_cancel(ldb);
+                       return WERR_INTERNAL_ERROR;
+               }
+       }
+
        ret = ldb_extended(ldb, DSDB_EXTENDED_REPLICATED_OBJECTS_OID, objects, &ext_res);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               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);
@@ -467,8 +560,30 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
        }
        talloc_free(ext_res);
 
+       /* Save our updated prefixMap */
+       if (working_schema) {
+               werr = dsdb_write_prefixes_from_schema_to_ldb(working_schema,
+                                                             ldb,
+                                                             working_schema);
+               if (!W_ERROR_IS_OK(werr)) {
+                       /* restore previous schema */
+                       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)));
+                       return werr;
+               }
+       }
+
        ret = ldb_transaction_prepare_commit(ldb);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               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)));
                return WERR_FOOBAR;
@@ -476,6 +591,11 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
 
        ret = dsdb_load_partition_usn(ldb, objects->partition_dn, &seq_num2, NULL);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               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);
                return WERR_FOOBAR;             
@@ -490,10 +610,27 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
 
        ret = ldb_transaction_commit(ldb);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               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"));
                return WERR_FOOBAR;
        }
 
+       /*
+        * Reset the Schema used by ldb. This will lead to
+        * 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);
+               }
+       }
 
        DEBUG(2,("Replicated %u objects (%u linked attributes) for %s\n",
                 objects->num_objects, objects->linked_attributes_count,
@@ -553,6 +690,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;
@@ -611,6 +749,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;