summaryrefslogtreecommitdiffstats
path: root/source3/rpc_client/cli_mdssvc.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source3/rpc_client/cli_mdssvc.c1214
1 files changed, 1214 insertions, 0 deletions
diff --git a/source3/rpc_client/cli_mdssvc.c b/source3/rpc_client/cli_mdssvc.c
new file mode 100644
index 0000000..93e032f
--- /dev/null
+++ b/source3/rpc_client/cli_mdssvc.c
@@ -0,0 +1,1214 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight client functions
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_client.h"
+#include "../librpc/gen_ndr/ndr_mdssvc_c.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "rpc_server/mdssvc/dalloc.h"
+#include "rpc_server/mdssvc/marshalling.h"
+#include "cli_mdssvc.h"
+#include "cli_mdssvc_private.h"
+#include "cli_mdssvc_util.h"
+
+struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx)
+{
+ mdscli_ctx->ctx_id.id++;
+ return mdscli_ctx->ctx_id;
+}
+
+char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx)
+{
+ return talloc_strdup(mem_ctx, mdscli_ctx->mdscmd_open.share_path);
+}
+
+struct mdscli_connect_state {
+ struct tevent_context *ev;
+ struct mdscli_ctx *mdscli_ctx;
+ struct mdssvc_blob response_blob;
+};
+
+static void mdscli_connect_open_done(struct tevent_req *subreq);
+static void mdscli_connect_unknown1_done(struct tevent_req *subreq);
+static void mdscli_connect_fetch_props_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *bh,
+ const char *share_name,
+ const char *mount_path)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_connect_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mdscli_ctx *ctx = NULL;
+
+ req = tevent_req_create(req, &state, struct mdscli_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ctx = talloc_zero(state, struct mdscli_ctx);
+ if (tevent_req_nomem(ctx, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ *state = (struct mdscli_connect_state) {
+ .ev = ev,
+ .mdscli_ctx = ctx,
+ };
+
+ *ctx = (struct mdscli_ctx) {
+ .bh = bh,
+ .max_fragment_size = 64 * 1024,
+ /*
+ * The connection id is a per tcon value sent by the client,
+ * 0x6b000060 is a value used most of the times for the first
+ * tcon.
+ */
+ .ctx_id.connection = UINT64_C(0x6b000060),
+ };
+
+ subreq = dcerpc_mdssvc_open_send(state,
+ state->ev,
+ ctx->bh,
+ &ctx->dev,
+ &ctx->mdscmd_open.unkn2,
+ &ctx->mdscmd_open.unkn3,
+ mount_path,
+ share_name,
+ ctx->mdscmd_open.share_path,
+ &ctx->ph);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, state->ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_connect_open_done, req);
+ ctx->async_pending++;
+
+ return req;
+}
+
+static void mdscli_connect_open_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+ size_t share_path_len;
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_open_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ share_path_len = strlen(mdscli_ctx->mdscmd_open.share_path);
+ if (share_path_len < 1 || share_path_len > UINT16_MAX) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ mdscli_ctx->mdscmd_open.share_path_len = share_path_len;
+
+ if (mdscli_ctx->mdscmd_open.share_path[share_path_len-1] == '/') {
+ mdscli_ctx->mdscmd_open.share_path[share_path_len-1] = '\0';
+ mdscli_ctx->mdscmd_open.share_path_len--;
+ }
+
+ subreq = dcerpc_mdssvc_unknown1_send(
+ state,
+ state->ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ geteuid(),
+ getegid(),
+ &mdscli_ctx->mdscmd_unknown1.status,
+ &mdscli_ctx->flags,
+ &mdscli_ctx->mdscmd_unknown1.unkn7);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, mdscli_connect_unknown1_done, req);
+}
+
+static void mdscli_connect_unknown1_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+ struct mdssvc_blob request_blob;
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_unknown1_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = mdscli_blob_fetch_props(state,
+ state->mdscli_ctx,
+ &request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ state->ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, mdscli_connect_fetch_props_done, req);
+ mdscli_ctx->async_pending++;
+ return;
+}
+
+static void mdscli_connect_fetch_props_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+ DALLOC_CTX *d = NULL;
+ sl_array_t *path_scope_array = NULL;
+ char *path_scope = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_blob.spotlight_blob,
+ state->response_blob.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ path_scope_array = dalloc_value_for_key(d,
+ "DALLOC_CTX", 0,
+ "kMDSStorePathScopes",
+ "sl_array_t");
+ if (path_scope_array == NULL) {
+ DBG_ERR("Missing kMDSStorePathScopes\n");
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ path_scope = dalloc_get(path_scope_array, "char *", 0);
+ if (path_scope == NULL) {
+ DBG_ERR("Missing path in kMDSStorePathScopes\n");
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ mdscli_ctx->path_scope_len = strlen(path_scope);
+ if (mdscli_ctx->path_scope_len < 1 ||
+ mdscli_ctx->path_scope_len > UINT16_MAX)
+ {
+ DBG_ERR("Bad path_scope: %s\n", path_scope);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ mdscli_ctx->path_scope = talloc_strdup(mdscli_ctx, path_scope);
+ if (tevent_req_nomem(mdscli_ctx->path_scope, req)) {
+ return;
+ }
+
+ if (mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] == '/') {
+ mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] = '\0';
+ mdscli_ctx->path_scope_len--;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS mdscli_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx **mdscli_ctx)
+{
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *mdscli_ctx = talloc_move(mem_ctx, &state->mdscli_ctx);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bh,
+ const char *share_name,
+ const char *mount_path,
+ struct mdscli_ctx **mdscli_ctx)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_connect_send(frame,
+ ev,
+ bh,
+ share_name,
+ mount_path);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_connect_recv(req, mem_ctx, mdscli_ctx);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_search_state {
+ struct mdscli_search_ctx *search;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+};
+
+static void mdscli_search_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx,
+ const char *mds_query,
+ const char *path_scope_in,
+ bool live)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_search_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ char *path_scope = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_search_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ search = talloc_zero(state, struct mdscli_search_ctx);
+ if (tevent_req_nomem(search, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (path_scope_in[0] == '/') {
+ path_scope = talloc_strdup(search, path_scope_in);
+ } else {
+ path_scope = talloc_asprintf(search,
+ "%s/%s",
+ mdscli_ctx->mdscmd_open.share_path,
+ path_scope_in);
+ }
+ if (tevent_req_nomem(path_scope, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ *search = (struct mdscli_search_ctx) {
+ .mdscli_ctx = mdscli_ctx,
+ .ctx_id = mdscli_new_ctx_id(mdscli_ctx),
+ .unique_id = generate_random_u64(),
+ .live = live,
+ .path_scope = path_scope,
+ .mds_query = talloc_strdup(search, mds_query),
+ };
+ if (tevent_req_nomem(search->mds_query, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ *state = (struct mdscli_search_state) {
+ .search = search,
+ };
+
+ status = mdscli_blob_search(state,
+ search,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_search_cmd_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_search_cmd_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_search_state *state = tevent_req_data(
+ req, struct mdscli_search_state);
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->search->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_blob.spotlight_blob,
+ state->response_blob.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ uint64p = dalloc_get(d,
+ "DALLOC_CTX", 0,
+ "uint64_t", 0);
+ if (uint64p == NULL) {
+ DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (*uint64p != 0) {
+ DBG_DEBUG("Unexpected mds result: 0x%" PRIx64 "\n", *uint64p);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_search_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx **search)
+{
+ struct mdscli_search_state *state = tevent_req_data(
+ req, struct mdscli_search_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *search = talloc_move(mem_ctx, &state->search);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ const char *mds_query,
+ const char *path_scope,
+ bool live,
+ struct mdscli_search_ctx **search)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_search_send(frame,
+ ev,
+ mdscli_ctx,
+ mds_query,
+ path_scope,
+ live);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_search_recv(req, mem_ctx, search);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_get_results_state {
+ struct tevent_context *ev;
+ struct mdscli_search_ctx *search;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_fragment;
+ DATA_BLOB response_data;
+ uint64_t *cnids;
+ uint32_t fragment;
+};
+
+static void mdscli_get_results_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_get_results_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_search_ctx *search)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_get_results_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mdscli_ctx *mdscli_ctx = search->mdscli_ctx;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_get_results_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ *state = (struct mdscli_get_results_state) {
+ .ev = ev,
+ .search = search,
+ };
+
+ status = mdscli_blob_get_results(state,
+ search,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &state->fragment,
+ &state->response_fragment,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_get_results_cmd_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_get_results_cmd_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_get_results_state *state = tevent_req_data(
+ req, struct mdscli_get_results_state);
+ struct mdscli_ctx *mdscli_ctx = state->search->mdscli_ctx;
+ size_t oldsize, newsize;
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ bool search_in_progress = false;
+ sl_cnids_t *cnids = NULL;
+ size_t ncnids;
+ size_t i;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->search->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ oldsize = state->response_data.length;
+ newsize = oldsize + state->response_fragment.length;
+ if (newsize < oldsize) {
+ tevent_req_nterror(req, NT_STATUS_INTEGER_OVERFLOW);
+ return;
+ }
+
+ ok = data_blob_realloc(state, &state->response_data, newsize);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ (void)memcpy(state->response_data.data + oldsize,
+ state->response_fragment.spotlight_blob,
+ state->response_fragment.length);
+
+ TALLOC_FREE(state->response_fragment.spotlight_blob);
+ state->response_fragment.length = 0;
+ state->response_fragment.size = 0;
+
+ if (state->fragment != 0) {
+ subreq = dcerpc_mdssvc_cmd_send(
+ state,
+ state->ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 1,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &state->fragment,
+ &state->response_fragment,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ tevent_req_post(req, state->ev);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ mdscli_get_results_cmd_done,
+ req);
+ mdscli_ctx->async_pending++;
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_data.data,
+ state->response_data.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ uint64p = dalloc_get(d,
+ "DALLOC_CTX", 0,
+ "uint64_t", 0);
+ if (uint64p == NULL) {
+ DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (*uint64p == 35) {
+ DBG_DEBUG("Search in progress\n");
+ search_in_progress = true;
+ }
+
+ cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1);
+ if (cnids == NULL) {
+ DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ ncnids = dalloc_size(cnids->ca_cnids);
+ if (ncnids == 0 && !search_in_progress) {
+ tevent_req_nterror(req, NT_STATUS_NO_MORE_MATCHES);
+ return;
+ }
+
+ if (cnids->ca_unkn1 != 0xadd) {
+ /*
+ * Whatever 0xadd means... but it seems to be the standard value
+ * macOS mdssvc returns here.
+ */
+ DBG_DEBUG("unexpected ca_unkn1: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (cnids->ca_context != state->search->ctx_id.connection ) {
+ DBG_DEBUG("unexpected ca_context: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ state->cnids = talloc_zero_array(state, uint64_t, ncnids);
+ if (tevent_req_nomem(state->cnids, req)) {
+ return;
+ }
+
+ for (i = 0; i < ncnids; i++) {
+ uint64_t *cnid = NULL;
+
+ cnid = dalloc_get(cnids->ca_cnids, "uint64_t", i);
+ if (cnid == NULL) {
+ DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ state->cnids[i] = *cnid;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint64_t **cnids)
+{
+ struct mdscli_get_results_state *state = tevent_req_data(
+ req, struct mdscli_get_results_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *cnids = talloc_move(mem_ctx, &state->cnids);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ uint64_t **_cnids)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (search->mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_get_results_send(frame,
+ ev,
+ search);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_get_results_recv(req, mem_ctx, _cnids);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_get_path_state {
+ struct mdscli_ctx *mdscli_ctx;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+ char *path;
+};
+
+static void mdscli_get_path_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_get_path_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_get_path_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mdscli_get_path_state) {
+ .mdscli_ctx = mdscli_ctx,
+ };
+
+ status = mdscli_blob_get_path(state,
+ mdscli_ctx,
+ cnid,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_get_path_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_get_path_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_get_path_state *state = tevent_req_data(
+ req, struct mdscli_get_path_state);
+ DALLOC_CTX *d = NULL;
+ size_t pathlen;
+ size_t prefixlen;
+ char *path = NULL;
+ const char *p = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_blob.spotlight_blob,
+ state->response_blob.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ path = dalloc_get(d,
+ "DALLOC_CTX", 0,
+ "DALLOC_CTX", 2,
+ "DALLOC_CTX", 0,
+ "DALLOC_CTX", 1,
+ "char *", 0);
+ if (path == NULL) {
+ DBG_DEBUG("No path in mds response: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ /* Path is prefixed by /PATHSCOPE/SHARENAME/, strip it */
+ pathlen = strlen(path);
+
+ /*
+ * path_scope_len and share_path_len are already checked to be smaller
+ * then UINT16_MAX so this can't overflow
+ */
+ prefixlen = state->mdscli_ctx->path_scope_len
+ + state->mdscli_ctx->mdscmd_open.share_path_len;
+
+ if (pathlen < prefixlen) {
+ DBG_DEBUG("Bad path: %s\n", path);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ p = path + prefixlen;
+ while (*p == '/') {
+ p++;
+ }
+ if (*p == '\0') {
+ DBG_DEBUG("Bad path: %s\n", path);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ state->path = talloc_strdup(state, p);
+ if (state->path == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ DBG_DEBUG("path: %s\n", state->path);
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **path)
+{
+ struct mdscli_get_path_state *state = tevent_req_data(
+ req, struct mdscli_get_path_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *path = talloc_move(mem_ctx, &state->path);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid,
+ char **path)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_get_path_send(frame, ev, mdscli_ctx, cnid);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_get_path_recv(req, mem_ctx, path);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_close_search_state {
+ struct mdscli_search_ctx *search;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+};
+
+static void mdscli_close_search_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_search_ctx **search)
+{
+ struct mdscli_ctx *mdscli_ctx = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_close_search_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_close_search_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mdscli_close_search_state) {
+ .search = talloc_move(state, search),
+ };
+ mdscli_ctx = state->search->mdscli_ctx;
+
+ status = mdscli_blob_close_search(state,
+ state->search,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_close_search_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_close_search_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_close_search_state *state = tevent_req_data(
+ req, struct mdscli_close_search_state);
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->search->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_close_search_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if ((*search)->mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_close_search_send(frame, ev, search);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_close_search_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_disconnect_state {
+ struct mdscli_ctx *mdscli_ctx;
+};
+
+static void mdscli_disconnect_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_disconnect_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(req, &state, struct mdscli_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mdscli_disconnect_state) {
+ .mdscli_ctx = mdscli_ctx,
+ };
+
+ subreq = dcerpc_mdssvc_close_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ &mdscli_ctx->ph,
+ &mdscli_ctx->mdscmd_close.status);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_disconnect_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_disconnect_state *state = tevent_req_data(
+ req, struct mdscli_disconnect_state);
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_close_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_disconnect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_disconnect_send(frame, ev, mdscli_ctx);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_disconnect_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}