drepl_server: Allow refresh of partitions on UpdateRef
[nivanova/samba-autobuild/.git] / source4 / rpc_server / drsuapi / getncchanges.c
index d334de7d0250809d629af4d2c13093c8870560f3..1f02f2c0936ae7b298a6f9176d492015e348f840 100644 (file)
@@ -135,7 +135,6 @@ static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
                return true;
        }
        return false;
-
 }
 
 static int uint32_t_cmp(uint32_t a1, uint32_t a2)
@@ -190,17 +189,39 @@ fail:
        }
 }
 
+/*
+ * Similar to function in repl_meta_data without the extra
+ * dependencies.
+ */
+static WERROR get_parsed_dns_trusted(TALLOC_CTX *mem_ctx, struct ldb_message_element *el,
+                                 struct parsed_dn **pdn)
+{
+       /* Here we get a list of 'struct parsed_dns' without the parsing */
+       int i;
+       *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
+                                el->num_values);
+       if (!*pdn) {
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       for (i = 0; i < el->num_values; i++) {
+               (*pdn)[i].v = &el->values[i];
+       }
+
+       return WERR_OK;
+}
+
 static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
                                                TALLOC_CTX *mem_ctx,
                                                struct ldb_message **msg,
                                                struct ldb_dn *object_dn,
+                                               const struct GUID *object_guid,
                                                const struct dsdb_attribute *sa,
                                                struct replPropertyMetaData1 *meta_data,
                                                struct ldb_message *revealed_users)
 {
        enum ndr_err_code ndr_err;
        int ldb_err;
-       unsigned i;
        char *attr_str = NULL;
        char *attr_hex = NULL;
        DATA_BLOB attr_blob;
@@ -231,30 +252,59 @@ static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
        existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
        if (existing != NULL) {
                /* Replace the old value (if one exists) with the current one */
-               for (i = 0; i < existing->num_values; i++) {
-                       struct dsdb_dn *existing_dn = dsdb_dn_parse_trusted(mem_ctx, sam_ctx, &existing->values[i], DSDB_SYNTAX_BINARY_DN);
-                       if (ldb_dn_compare(object_dn, existing_dn->dn) == 0) {
-                               struct replPropertyMetaData1 existing_meta_data;
-                               ndr_err = ndr_pull_struct_blob_all_noalloc(&existing_dn->extra_part,
-                                                              &existing_meta_data,
-                                                              (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
-                               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               struct parsed_dn *link_dns;
+               struct parsed_dn *exact = NULL, *unused = NULL;
+               WERROR werr;
+               uint8_t attid[4];
+               DATA_BLOB partial_meta;
+
+               werr = get_parsed_dns_trusted(mem_ctx, existing, &link_dns);
+               if (!W_ERROR_IS_OK(werr)) {
+                       return werr;
+               }
+
+               /* Construct a partial metadata blob to match on in the DB */
+               SIVAL(attid, 0, sa->attributeID_id);
+               partial_meta.length = 4;
+               partial_meta.data = attid;
+
+               /* Binary search using GUID and attribute id for uniqueness */
+               ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values,
+                                        object_guid, object_dn,
+                                        partial_meta, 4,
+                                        &exact, &unused,
+                                        DSDB_SYNTAX_BINARY_DN, true);
+
+               if (ldb_err != LDB_SUCCESS) {
+                       DEBUG(0,(__location__ ": Failed parsed DN find - %s\n",
+                                ldb_errstring(sam_ctx)));
+                       return WERR_DS_DRA_INTERNAL_ERROR;
+               }
+
+               if (exact != NULL) {
+                       /* Perform some verification of the blob */
+                       struct replPropertyMetaData1 existing_meta_data;
+                       ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_dn->extra_part,
+                                                                  &existing_meta_data,
+                                                                  (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
+                       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                               return WERR_DS_DRA_INTERNAL_ERROR;
+                       }
+
+                       if (existing_meta_data.attid == sa->attributeID_id) {
+                               ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
+                               if (ldb_err != LDB_SUCCESS) {
                                        return WERR_DS_DRA_INTERNAL_ERROR;
                                }
 
-                               if (existing_meta_data.attid == sa->attributeID_id) {
-                                       ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
-                                       if (ldb_err != LDB_SUCCESS) {
-                                               return WERR_DS_DRA_INTERNAL_ERROR;
-                                       }
-
-                                       el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
-                                       if (el_del->values == NULL) {
-                                               return WERR_NOT_ENOUGH_MEMORY;
-                                       }
-                                       el_del->values[0] = existing->values[i];
-                                       el_del->num_values = 1;
+                               el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
+                               if (el_del->values == NULL) {
+                                       return WERR_NOT_ENOUGH_MEMORY;
                                }
+                               el_del->values[0] = *exact->v;
+                               el_del->num_values = 1;
+                       } else {
+                               return WERR_DS_DRA_INTERNAL_ERROR;
                        }
                }
        }
@@ -287,6 +337,7 @@ static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItem
                                          struct replPropertyMetaDataBlob md,
                                          struct ldb_context *sam_ctx,
                                          const struct ldb_message *msg,
+                                         const struct GUID *guid,
                                          uint32_t *count,
                                          uint64_t highest_usn,
                                          const struct dsdb_attribute *rdn_sa,
@@ -338,7 +389,7 @@ static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItem
                                 sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
                        werr = getncchanges_update_revealed_list(sam_ctx, obj,
                                                                 revealed_list_msg,
-                                                                msg->dn, sa,
+                                                                msg->dn, guid, sa,
                                                                 &md.ctr.ctr1.array[i],
                                                                 existing_revealed_list_msg);
                        if (!W_ERROR_IS_OK(werr)) {
@@ -396,7 +447,8 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                                          enum drsuapi_DsExtendedOperation extended_op,
                                          bool force_object_return,
                                          uint32_t *local_pas,
-                                         struct ldb_dn *machine_dn)
+                                         struct ldb_dn *machine_dn,
+                                         const struct GUID *guid)
 {
        const struct ldb_val *md_value;
        uint32_t i, n;
@@ -510,7 +562,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                        return WERR_DS_DRA_INTERNAL_ERROR;
                }
 
-               ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, 0);
+               ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
                if (ldb_err != LDB_SUCCESS || res->count != 1) {
                        ldb_transaction_cancel(sam_ctx);
                        return WERR_DS_DRA_INTERNAL_ERROR;
@@ -518,8 +570,9 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
 
                existing_revealed_list_msg = res->msgs[0];
 
-               werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, &n,
-                                                  highest_usn, rdn_sa, schema,
+               werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg,
+                                                  guid, &n, highest_usn,
+                                                  rdn_sa, schema,
                                                   uptodateness_vector,
                                                   partial_attribute_set, local_pas,
                                                   attids,
@@ -548,9 +601,9 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                        return WERR_DS_DRA_INTERNAL_ERROR;
                }
        } else {
-               werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, &n,
-                                                  highest_usn, rdn_sa, schema,
-                                                  uptodateness_vector,
+               werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid,
+                                                  &n, highest_usn, rdn_sa,
+                                                  schema, uptodateness_vector,
                                                   partial_attribute_set, local_pas,
                                                   attids,
                                                   false,
@@ -1128,17 +1181,19 @@ static WERROR samdb_result_sid_array_dn(struct ldb_context *sam_ctx,
 
 
 /*
-  return an array of SIDs from a ldb_message given an attribute name
-  assumes the SIDs are in NDR form
+ * Return an array of SIDs from a ldb_message given an attribute name assumes
+ * the SIDs are in NDR form (with additional sids applied on the end).
  */
 static WERROR samdb_result_sid_array_ndr(struct ldb_context *sam_ctx,
                                         struct ldb_message *msg,
                                         TALLOC_CTX *mem_ctx,
                                         const char *attr,
-                                        const struct dom_sid ***sids)
+                                        const struct dom_sid ***sids,
+                                        const struct dom_sid **additional_sids,
+                                        unsigned int num_additional)
 {
        struct ldb_message_element *el;
-       unsigned int i;
+       unsigned int i, j;
 
        el = ldb_msg_find_element(msg, attr);
        if (!el) {
@@ -1146,7 +1201,9 @@ static WERROR samdb_result_sid_array_ndr(struct ldb_context *sam_ctx,
                return WERR_OK;
        }
 
-       (*sids) = talloc_array(mem_ctx, const struct dom_sid *, el->num_values + 1);
+       /* Make array long enough for NULL and additional SID */
+       (*sids) = talloc_array(mem_ctx, const struct dom_sid *,
+                              el->num_values + num_additional + 1);
        W_ERROR_HAVE_NO_MEMORY(*sids);
 
        for (i=0; i<el->num_values; i++) {
@@ -1163,6 +1220,11 @@ static WERROR samdb_result_sid_array_ndr(struct ldb_context *sam_ctx,
                }
                (*sids)[i] = sid;
        }
+
+       for (j = 0; j < num_additional; j++) {
+               (*sids)[i++] = additional_sids[j];
+       }
+
        (*sids)[i] = NULL;
 
        return WERR_OK;
@@ -1203,9 +1265,11 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
        int ret;
        const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", "objectGUID", NULL };
        const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
-       struct ldb_result *rodc_res, *obj_res;
+       struct ldb_result *rodc_res = NULL, *obj_res = NULL;
        const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids;
+       const struct dom_sid *object_sid = NULL;
        WERROR werr;
+       const struct dom_sid *additional_sids[] = { NULL, NULL };
 
        DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n",
                 drs_ObjectIdentifier_to_string(mem_ctx, ncRoot)));
@@ -1269,13 +1333,13 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
         * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC)
         * then you can do EXOP_REPL_SECRETS
         */
+       obj_dn = drs_ObjectIdentifier_to_dn(mem_ctx, b_state->sam_ctx_system, ncRoot);
+       if (!ldb_dn_validate(obj_dn)) goto failed;
+
        if (has_get_all_changes) {
                goto allowed;
        }
 
-       obj_dn = drs_ObjectIdentifier_to_dn(mem_ctx, b_state->sam_ctx_system, ncRoot);
-       if (!ldb_dn_validate(obj_dn)) goto failed;
-
        rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "<SID=%s>",
                                 dom_sid_string(mem_ctx, user_sid));
        if (!ldb_dn_validate(rodc_dn)) goto failed;
@@ -1289,11 +1353,13 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
        if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed;
 
        /* if the object SID is equal to the user_sid, allow */
-       if (dom_sid_equal(user_sid,
-                         samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid"))) {
+       object_sid = samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid");
+       if (dom_sid_equal(user_sid, object_sid)) {
                goto allowed;
        }
 
+       additional_sids[0] = object_sid;
+
        /*
         * Must be an RODC account at this point, verify machine DN matches the
         * SID account
@@ -1334,8 +1400,14 @@ static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
                goto denied;
        }
 
+       /*
+        * The SID list needs to include itself as well as the tokenGroups.
+        *
+        * TODO determine if sIDHistory is required for this check
+        */
        werr = samdb_result_sid_array_ndr(b_state->sam_ctx_system, obj_res->msgs[0],
-                                        mem_ctx, "tokenGroups", &token_sids);
+                                         mem_ctx, "tokenGroups", &token_sids,
+                                         additional_sids, 1);
        if (!W_ERROR_IS_OK(werr) || token_sids==NULL) {
                goto denied;
        }
@@ -1360,7 +1432,7 @@ denied:
 allowed:
        DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
                 ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC",
-                ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
+                ldb_dn_get_linearized(*machine_dn)));
        ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
        req10->highwatermark.highest_usn = 0;
        return WERR_OK;
@@ -2652,7 +2724,8 @@ allowed:
                                                   req10->uptodateness_vector,
                                                   req10->extended_op,
                                                   max_wait_reached,
-                                                  local_pas, machine_dn);
+                                                  local_pas, machine_dn,
+                                                  &getnc_state->guids[i]);
                if (!W_ERROR_IS_OK(werr)) {
                        return werr;
                }
@@ -2764,7 +2837,8 @@ allowed:
                                                           req10->extended_op,
                                                           false, /* force_object_return */
                                                           local_pas,
-                                                          machine_dn);
+                                                          machine_dn,
+                                                          next_anc_guid);
                        if (!W_ERROR_IS_OK(werr)) {
                                return werr;
                        }
@@ -2859,7 +2933,9 @@ allowed:
                   to send notifies using the GC SPN */
                ureq.options |= (req10->replica_flags & DRSUAPI_DRS_REF_GCSPN);
 
-               werr = drsuapi_UpdateRefs(b_state, mem_ctx, &ureq);
+               werr = drsuapi_UpdateRefs(dce_call->msg_ctx,
+                                         dce_call->event_ctx, b_state,
+                                         mem_ctx, &ureq);
                if (!W_ERROR_IS_OK(werr)) {
                        DEBUG(0,(__location__ ": Failed UpdateRefs on %s for %s in DsGetNCChanges - %s\n",
                                 drs_ObjectIdentifier_to_string(mem_ctx, ncRoot), ureq.dest_dsa_dns_name,