s4-repl: Allow dsdb_replicated_objects_commit() to use different schema while committ...
[kai/samba.git] / source4 / dsdb / repl / drepl_out_helpers.c
index 55357509f09c7d2c2fe6f077d5ea6428ecbcdc47..8c5c9da6c3e16fe71708d8987171fdcda2ed2b75 100644 (file)
@@ -24,7 +24,6 @@
 #include "auth/auth.h"
 #include "smbd/service.h"
 #include "lib/events/events.h"
-#include "lib/messaging/irpc.h"
 #include "dsdb/repl/drepl_service.h"
 #include "lib/ldb/include/ldb_errors.h"
 #include "../lib/util/dlinklist.h"
@@ -260,6 +259,68 @@ static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
 
 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
 
+/*
+  get a partial attribute set for a replication call
+ */
+static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
+                                                       TALLOC_CTX *mem_ctx,
+                                                       struct drsuapi_DsPartialAttributeSet **_pas,
+                                                       bool for_schema)
+{
+       struct drsuapi_DsPartialAttributeSet *pas;
+       struct dsdb_schema *schema;
+       uint32_t i;
+
+       pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
+       NT_STATUS_HAVE_NO_MEMORY(pas);
+
+       schema = dsdb_get_schema(service->samdb, NULL);
+
+       pas->version = 1;
+       pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(pas->attids, pas);
+
+       for (i=0; i<schema->num_attributes; i++) {
+               struct dsdb_attribute *a;
+               a = schema->attributes_by_attributeID_id[i];
+                if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
+                       continue;
+               }
+               if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
+                       continue;
+               }
+               pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
+               pas->num_attids++;
+       }
+       *_pas = pas;
+       return NT_STATUS_OK;
+}
+
+/*
+  convert from one udv format to the other
+ */
+static WERROR udv_convert(TALLOC_CTX *mem_ctx,
+                         const struct replUpToDateVectorCtr2 *udv,
+                         struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
+{
+       uint32_t i;
+
+       udv_ex->version = 2;
+       udv_ex->reserved1 = 0;
+       udv_ex->reserved2 = 0;
+       udv_ex->count = udv->count;
+       udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
+       W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
+
+       for (i=0; i<udv->count; i++) {
+               udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
+               udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
+       }
+
+       return WERR_OK;
+}
+
+
 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
 {
        struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
@@ -271,17 +332,9 @@ static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
        struct drsuapi_DsGetNCChanges *r;
        struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
        struct tevent_req *subreq;
-       int ret;
-
-       /* check that the client isn't lying about being a RODC */
-       ret = dsdb_validate_client_flags(service->samdb, rf1);
-       if (ret != LDB_SUCCESS) {
-               return;
-       }
-
-       if ((rf1->replica_flags & DRSUAPI_DRS_WRIT_REP) == 0) {
-               return;
-       }
+       struct drsuapi_DsPartialAttributeSet *pas = NULL;
+       NTSTATUS status;
+       uint32_t replica_flags;
 
        r = talloc(state, struct drsuapi_DsGetNCChanges);
        if (tevent_req_nomem(r, req)) {
@@ -301,12 +354,40 @@ static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
                return;
        }
 
+       if (partition->uptodatevector.count != 0 &&
+           partition->uptodatevector_ex.count == 0) {
+               WERROR werr;
+               werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
+               if (!W_ERROR_IS_OK(werr)) {
+                       DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
+                                ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
+               }
+       }
+
        if (partition->uptodatevector_ex.count == 0) {
                uptodateness_vector = NULL;
        } else {
                uptodateness_vector = &partition->uptodatevector_ex;
        }
 
+       replica_flags = rf1->replica_flags;
+
+       if (service->am_rodc) {
+               bool for_schema = false;
+               if (ldb_dn_compare_base(ldb_get_schema_basedn(service->samdb), partition->dn) == 0) {
+                       for_schema = true;
+               }
+
+               status = dreplsrv_get_rodc_partial_attribute_set(service, r, &pas, for_schema);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,(__location__ ": Failed to construct partial attribute set : %s\n", nt_errstr(status)));
+                       return;
+               }
+               if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
+                       replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
+               }
+       }
+
        r->in.bind_handle       = &drsuapi->bind_handle;
        if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
                r->in.level                             = 8;
@@ -315,12 +396,12 @@ static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
                r->in.req->req8.naming_context          = &partition->nc;
                r->in.req->req8.highwatermark           = rf1->highwatermark;
                r->in.req->req8.uptodateness_vector     = uptodateness_vector;
-               r->in.req->req8.replica_flags           = rf1->replica_flags;
+               r->in.req->req8.replica_flags           = replica_flags;
                r->in.req->req8.max_object_count        = 133;
                r->in.req->req8.max_ndr_size            = 1336811;
                r->in.req->req8.extended_op             = state->op->extended_op;
                r->in.req->req8.fsmo_info               = state->op->fsmo_info;
-               r->in.req->req8.partial_attribute_set   = NULL;
+               r->in.req->req8.partial_attribute_set   = pas;
                r->in.req->req8.partial_attribute_set_ex= NULL;
                r->in.req->req8.mapping_ctr.num_mappings= 0;
                r->in.req->req8.mapping_ctr.mappings    = NULL;
@@ -331,7 +412,7 @@ static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
                r->in.req->req5.naming_context          = &partition->nc;
                r->in.req->req5.highwatermark           = rf1->highwatermark;
                r->in.req->req5.uptodateness_vector     = uptodateness_vector;
-               r->in.req->req5.replica_flags           = rf1->replica_flags;
+               r->in.req->req5.replica_flags           = replica_flags;
                r->in.req->req5.max_object_count        = 133;
                r->in.req->req5.max_ndr_size            = 1336770;
                r->in.req->req5.extended_op             = state->op->extended_op;
@@ -371,7 +452,7 @@ static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
        uint32_t ctr_level = 0;
        struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
        struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
-
+       enum drsuapi_DsExtendedError extended_ret;
        state->ndr_struct_ptr = NULL;
 
        status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
@@ -426,6 +507,21 @@ static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
                        tevent_req_nterror(req, status);
                        return;
                }
+               extended_ret = ctr6->extended_ret;
+       }
+
+       if (ctr_level == 1) {
+               extended_ret = ctr1->extended_ret;
+       }
+
+       if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
+               state->op->extended_ret = extended_ret;
+
+               if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+                       status = NT_STATUS_UNSUCCESSFUL;
+                       tevent_req_nterror(req, status);
+                       return;
+               }
        }
 
        dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
@@ -445,6 +541,8 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
        struct dreplsrv_service *service = state->op->service;
        struct dreplsrv_partition *partition = state->op->source_dsa->partition;
        struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
+       struct dsdb_schema *schema;
+       struct dsdb_schema *working_schema;
        const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
        uint32_t object_count;
        struct drsuapi_DsReplicaObjectListItemEx *first_object;
@@ -483,17 +581,45 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
                return;
        }
 
-       status = dsdb_extended_replicated_objects_convert(service->samdb,
-                                                         partition->nc.dn,
-                                                         mapping_ctr,
-                                                         object_count,
-                                                         first_object,
-                                                         linked_attributes_count,
-                                                         linked_attributes,
-                                                         &rf1,
-                                                         uptodateness_vector,
-                                                         &drsuapi->gensec_skey,
-                                                         state, &objects);
+       schema = dsdb_get_schema(service->samdb, NULL);
+       if (!schema) {
+               DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       /* Decide what working schema to use for object conversion */
+       if (ldb_dn_compare(partition->dn, ldb_get_schema_basedn(service->samdb)) == 0) {
+               /* create working schema to convert objects with */
+               status = dsdb_repl_make_working_schema(service->samdb,
+                                                      schema,
+                                                      mapping_ctr,
+                                                      object_count,
+                                                      first_object,
+                                                      &drsuapi->gensec_skey,
+                                                      state, &working_schema);
+               if (!W_ERROR_IS_OK(status)) {
+                       DEBUG(0,("Failed to create working schema: %s",
+                                win_errstr(status)));
+                       tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+                       return;
+               }
+       } else {
+               working_schema = schema;
+       }
+
+       status = dsdb_replicated_objects_convert(service->samdb,
+                                                working_schema,
+                                                partition->nc.dn,
+                                                mapping_ctr,
+                                                object_count,
+                                                first_object,
+                                                linked_attributes_count,
+                                                linked_attributes,
+                                                &rf1,
+                                                uptodateness_vector,
+                                                &drsuapi->gensec_skey,
+                                                state, &objects);
        if (!W_ERROR_IS_OK(status)) {
                nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
                DEBUG(0,("Failed to convert objects: %s/%s\n",
@@ -502,9 +628,10 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
                return;
        }
 
-       status = dsdb_extended_replicated_objects_commit(service->samdb,
-                                                        objects, 
-                                                        &state->op->source_dsa->notify_uSN);
+       status = dsdb_replicated_objects_commit(service->samdb,
+                                               NULL,
+                                               objects,
+                                               &state->op->source_dsa->notify_uSN);
        talloc_free(objects);
        if (!W_ERROR_IS_OK(status)) {
                nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
@@ -514,9 +641,10 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
                return;
        }
 
-       /* if it applied fine, we need to update the highwatermark */
-       *state->op->source_dsa->repsFrom1 = rf1;
-
+       if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
+               /* if it applied fine, we need to update the highwatermark */
+               *state->op->source_dsa->repsFrom1 = rf1;
+       }
        /*
         * TODO: update our uptodatevector!
         */
@@ -529,6 +657,16 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
                return;
        }
 
+       if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
+           state->op->service->am_rodc) {
+               /*
+                 we don't do the UpdateRefs for extended ops or if we
+                 are a RODC
+                */
+               tevent_req_done(req);
+               return;
+       }
+
        /* now we need to update the repsTo record for this partition
           on the server. These records are initially established when
           we join the domain, but they quickly expire.  We do it here
@@ -553,8 +691,6 @@ static void dreplsrv_update_refs_trigger(struct tevent_req *req)
        char *ntds_guid_str;
        char *ntds_dns_name;
        struct tevent_req *subreq;
-       bool am_rodc;
-       int ret;
 
        r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
        if (tevent_req_nomem(r, req)) {
@@ -568,7 +704,7 @@ static void dreplsrv_update_refs_trigger(struct tevent_req *req)
 
        ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
                                        ntds_guid_str,
-                                       lp_dnsdomain(service->task->lp_ctx));
+                                       lpcfg_dnsdomain(service->task->lp_ctx));
        if (tevent_req_nomem(ntds_dns_name, req)) {
                return;
        }
@@ -579,8 +715,7 @@ static void dreplsrv_update_refs_trigger(struct tevent_req *req)
        r->in.req.req1.dest_dsa_dns_name  = ntds_dns_name;
        r->in.req.req1.dest_dsa_guid      = service->ntds_guid;
        r->in.req.req1.options            = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
-       ret = samdb_rodc(service->samdb, &am_rodc);
-       if (ret == LDB_SUCCESS && !am_rodc) {
+       if (!service->am_rodc) {
                r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
        }