s4-drs: added domain_sid to DRS security checks
[nivanova/samba-autobuild/.git] / source4 / rpc_server / drsuapi / getncchanges.c
index e70e863c368004796788d9a1c38ab7bb2e35faf2..dcf1dbef5c3304368f6c18d1518a633a231b7741 100644 (file)
@@ -32,6 +32,7 @@
 #include "libcli/security/security.h"
 #include "lib/util/binsearch.h"
 #include "lib/util/tsort.h"
+#include "auth/session.h"
 
 /*
   build a DsReplicaObjectIdentifier from a ldb msg
@@ -130,8 +131,12 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                return WERR_OK;
        }
 
-       ndr_err = ndr_pull_struct_blob(md_value, obj,
-                                      lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")), &md,
+       if (instanceType & INSTANCE_TYPE_UNINSTANT) {
+               /* don't send uninstantiated objects */
+               return WERR_OK;
+       }
+
+       ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
                                       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return WERR_DS_DRA_INTERNAL_ERROR;
@@ -199,6 +204,19 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                        continue;
                }
 
+               /*
+                * If the recipient is a RODC, then we should not add any
+                * RODC filtered attribute
+                *
+                * TODO: This is not strictly correct, as it doesn't allow for administrators
+                * to setup some users to transfer passwords to specific RODCs. To support that
+                * we would instead remove this check and rely on extended ACL checking in the dsdb
+                * acl module.
+                */
+               if (dsdb_attr_in_rodc_fas(replica_flags, sa)) {
+                       continue;
+               }
+
                obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
                obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
                obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
@@ -647,6 +665,8 @@ struct drsuapi_getncchanges_state {
        struct ldb_dn *last_dn;
        struct drsuapi_DsReplicaLinkedAttribute *la_list;
        uint32_t la_count;
+       bool la_sorted;
+       uint32_t la_idx;
        struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
 };
 
@@ -683,7 +703,13 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        struct drsuapi_DsGetNCChangesRequest8 *req8;
        uint32_t options;
        uint32_t max_objects;
+       uint32_t max_links;
+       uint32_t link_count = 0;
+       uint32_t link_total = 0;
+       uint32_t link_given = 0;
        struct ldb_dn *search_dn = NULL;
+       bool am_rodc;
+       enum security_user_level security_level;
 
        DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
        b_state = h->data;
@@ -699,7 +725,8 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        r->out.ctr->ctr6.uptodateness_vector = NULL;
 
        /* a RODC doesn't allow for any replication */
-       if (samdb_rodc(b_state->sam_ctx)) {
+       ret = samdb_rodc(b_state->sam_ctx, &am_rodc);
+       if (ret == LDB_SUCCESS && am_rodc) {
                DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
                return WERR_DS_DRA_SOURCE_DISABLED;
        }
@@ -733,17 +760,30 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                return WERR_DS_DRA_SOURCE_DISABLED;
        }
 
+       werr = drs_security_level_check(dce_call, "DsGetNCChanges", SECURITY_RO_DOMAIN_CONTROLLER,
+                                       samdb_domain_sid(b_state->sam_ctx));
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+       /* for non-administrator replications, check that they have
+          given the correct source_dsa_invocation_id */
+       security_level = security_session_user_level(dce_call->conn->auth_state.session_info,
+                                                    samdb_domain_sid(b_state->sam_ctx));
+       if (security_level == SECURITY_RO_DOMAIN_CONTROLLER &&
+           (req8->replica_flags & DRSUAPI_DRS_WRIT_REP)) {
+               DEBUG(0,(__location__ ": Attempt to do writeable replication by RODC %s\n",
+                        dom_sid_string(mem_ctx,
+                                       dce_call->conn->auth_state.session_info->security_token->user_sid)));
+               return WERR_DS_DRA_INVALID_PARAMETER;
+       }
+
 
        if (req8->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
                /* Ignore the _in_ uptpdateness vector*/
                req8->uptodateness_vector = NULL;
        } 
 
-       werr = drs_security_level_check(dce_call, "DsGetNCChanges");
-       if (!W_ERROR_IS_OK(werr)) {
-               return werr;
-       }
-
        /* we don't yet support extended operations */
        switch (req8->extended_op) {
        case DRSUAPI_EXOP_NONE:
@@ -812,7 +852,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                enum ldb_scope scope = LDB_SCOPE_SUBTREE;
                const char *extra_filter;
 
-               extra_filter = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
+               extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
 
                getnc_state->min_usn = req8->highwatermark.highest_usn;
 
@@ -902,10 +942,14 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        /* use this to force single objects at a time, which is useful
         * for working out what object is giving problems
         */
-       max_objects = lp_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
+       max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
        if (req8->max_object_count < max_objects) {
                max_objects = req8->max_object_count;
        }
+       /*
+        * TODO: work out how the maximum should be calculated
+        */
+       max_links = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max link sync", 1500);
 
        for(i=getnc_state->num_sent; 
            i<getnc_state->site_res->count && 
@@ -970,13 +1014,15 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
 
        /* the client can us to call UpdateRefs on its behalf to
           re-establish monitoring of the NC */
-       if ((req8->replica_flags & DRSUAPI_DRS_ADD_REF) && 
+       if ((req8->replica_flags & (DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_REF_GCSPN)) &&
            !GUID_all_zero(&req8->destination_dsa_guid)) {
                struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
+               DEBUG(3,("UpdateRefs on getncchanges for %s\n",
+                        GUID_string(mem_ctx, &req8->destination_dsa_guid)));
                ureq.naming_context = ncRoot;
                ureq.dest_dsa_dns_name = talloc_asprintf(mem_ctx, "%s._msdcs.%s",
                                                         GUID_string(mem_ctx, &req8->destination_dsa_guid),
-                                                        lp_realm(dce_call->conn->dce_ctx->lp_ctx));
+                                                        lpcfg_realm(dce_call->conn->dce_ctx->lp_ctx));
                if (!ureq.dest_dsa_dns_name) {
                        return WERR_NOMEM;
                }
@@ -991,14 +1037,46 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                }
        }
 
+       /*
+        * TODO:
+        * This is just a guess, how to calculate the
+        * number of linked attributes to send, we need to
+        * find out how to do this right.
+        */
+       if (r->out.ctr->ctr6.object_count >= max_links) {
+               max_links = 0;
+       } else {
+               max_links -= r->out.ctr->ctr6.object_count;
+       }
+
+       link_total = getnc_state->la_count;
+
        if (i < getnc_state->site_res->count) {
                r->out.ctr->ctr6.more_data = true;
        } else {
-               r->out.ctr->ctr6.linked_attributes_count = getnc_state->la_count;
-               r->out.ctr->ctr6.linked_attributes = talloc_steal(mem_ctx, getnc_state->la_list);
+               /* sort the whole array the first time */
+               if (!getnc_state->la_sorted) {
+                       LDB_TYPESAFE_QSORT(getnc_state->la_list, getnc_state->la_count,
+                                          b_state->sam_ctx, linked_attribute_compare);
+                       getnc_state->la_sorted = true;
+               }
+
+               link_count = getnc_state->la_count - getnc_state->la_idx;
+               link_count = MIN(max_links, link_count);
+
+               r->out.ctr->ctr6.linked_attributes_count = link_count;
+               r->out.ctr->ctr6.linked_attributes = getnc_state->la_list + getnc_state->la_idx;
+
+               getnc_state->la_idx += link_count;
+               link_given = getnc_state->la_idx;
 
-               LDB_TYPESAFE_QSORT(r->out.ctr->ctr6.linked_attributes, r->out.ctr->ctr6.linked_attributes_count,
-                                  b_state->sam_ctx, linked_attribute_compare);
+               if (getnc_state->la_idx < getnc_state->la_count) {
+                       r->out.ctr->ctr6.more_data = true;
+               }
+       }
+
+       if (!r->out.ctr->ctr6.more_data) {
+               talloc_steal(mem_ctx, getnc_state->la_list);
 
                r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
                r->out.ctr->ctr6.new_highwatermark.highest_usn = r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn;
@@ -1020,12 +1098,13 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        }
 
        DEBUG(r->out.ctr->ctr6.more_data?2:1,
-             ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
+             ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %u/%u) %u links (done %u/%u)\n",
               (unsigned long long)(req8->highwatermark.highest_usn+1),
-              req8->replica_flags,
-              ncRoot->dn, r->out.ctr->ctr6.object_count,
+              req8->replica_flags, ncRoot->dn,
+              r->out.ctr->ctr6.object_count,
               i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i,
-              r->out.ctr->ctr6.linked_attributes_count));
+              r->out.ctr->ctr6.linked_attributes_count,
+              link_given, link_total));
 
 #if 0
        if (!r->out.ctr->ctr6.more_data) {