Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Main metadata server / Spotlight client functions
4 :
5 : Copyright (C) Ralph Boehme 2019
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "rpc_client.h"
23 : #include "../librpc/gen_ndr/ndr_mdssvc_c.h"
24 : #include "lib/util/tevent_ntstatus.h"
25 : #include "rpc_server/mdssvc/dalloc.h"
26 : #include "rpc_server/mdssvc/marshalling.h"
27 : #include "cli_mdssvc.h"
28 : #include "cli_mdssvc_private.h"
29 : #include "cli_mdssvc_util.h"
30 :
31 32 : struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx)
32 : {
33 32 : mdscli_ctx->ctx_id.id++;
34 32 : return mdscli_ctx->ctx_id;
35 : }
36 :
37 6 : char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
38 : struct mdscli_ctx *mdscli_ctx)
39 : {
40 6 : return talloc_strdup(mem_ctx, mdscli_ctx->mdscmd_open.share_path);
41 : }
42 :
43 : struct mdscli_connect_state {
44 : struct tevent_context *ev;
45 : struct mdscli_ctx *mdscli_ctx;
46 : struct mdssvc_blob response_blob;
47 : };
48 :
49 : static void mdscli_connect_open_done(struct tevent_req *subreq);
50 : static void mdscli_connect_unknown1_done(struct tevent_req *subreq);
51 : static void mdscli_connect_fetch_props_done(struct tevent_req *subreq);
52 :
53 10 : struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
54 : struct tevent_context *ev,
55 : struct dcerpc_binding_handle *bh,
56 : const char *share_name,
57 : const char *mount_path)
58 : {
59 10 : struct tevent_req *req = NULL;
60 10 : struct mdscli_connect_state *state = NULL;
61 10 : struct tevent_req *subreq = NULL;
62 10 : struct mdscli_ctx *ctx = NULL;
63 :
64 10 : req = tevent_req_create(req, &state, struct mdscli_connect_state);
65 10 : if (req == NULL) {
66 0 : return NULL;
67 : }
68 :
69 10 : ctx = talloc_zero(state, struct mdscli_ctx);
70 10 : if (tevent_req_nomem(ctx, req)) {
71 0 : return tevent_req_post(req, ev);
72 : }
73 :
74 10 : *state = (struct mdscli_connect_state) {
75 : .ev = ev,
76 : .mdscli_ctx = ctx,
77 : };
78 :
79 10 : *ctx = (struct mdscli_ctx) {
80 : .bh = bh,
81 : .max_fragment_size = 64 * 1024,
82 : /*
83 : * The connection id is a per tcon value sent by the client,
84 : * 0x6b000060 is a value used most of the times for the first
85 : * tcon.
86 : */
87 : .ctx_id.connection = UINT64_C(0x6b000060),
88 : };
89 :
90 10 : subreq = dcerpc_mdssvc_open_send(state,
91 10 : state->ev,
92 : ctx->bh,
93 : &ctx->dev,
94 : &ctx->mdscmd_open.unkn2,
95 : &ctx->mdscmd_open.unkn3,
96 : mount_path,
97 : share_name,
98 10 : ctx->mdscmd_open.share_path,
99 : &ctx->ph);
100 10 : if (tevent_req_nomem(subreq, req)) {
101 0 : return tevent_req_post(req, state->ev);
102 : }
103 10 : tevent_req_set_callback(subreq, mdscli_connect_open_done, req);
104 10 : ctx->async_pending++;
105 :
106 10 : return req;
107 : }
108 :
109 10 : static void mdscli_connect_open_done(struct tevent_req *subreq)
110 : {
111 10 : struct tevent_req *req = tevent_req_callback_data(
112 : subreq, struct tevent_req);
113 10 : struct mdscli_connect_state *state = tevent_req_data(
114 : req, struct mdscli_connect_state);
115 10 : struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
116 : size_t share_path_len;
117 : NTSTATUS status;
118 :
119 10 : status = dcerpc_mdssvc_open_recv(subreq, state);
120 10 : TALLOC_FREE(subreq);
121 10 : state->mdscli_ctx->async_pending--;
122 10 : if (tevent_req_nterror(req, status)) {
123 0 : return;
124 : }
125 :
126 10 : share_path_len = strlen(mdscli_ctx->mdscmd_open.share_path);
127 10 : if (share_path_len < 1 || share_path_len > UINT16_MAX) {
128 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
129 0 : return;
130 : }
131 10 : mdscli_ctx->mdscmd_open.share_path_len = share_path_len;
132 :
133 10 : if (mdscli_ctx->mdscmd_open.share_path[share_path_len-1] == '/') {
134 0 : mdscli_ctx->mdscmd_open.share_path[share_path_len-1] = '\0';
135 0 : mdscli_ctx->mdscmd_open.share_path_len--;
136 : }
137 :
138 10 : subreq = dcerpc_mdssvc_unknown1_send(
139 : state,
140 : state->ev,
141 : mdscli_ctx->bh,
142 : &mdscli_ctx->ph,
143 : 0,
144 : mdscli_ctx->dev,
145 : mdscli_ctx->mdscmd_open.unkn2,
146 : 0,
147 : geteuid(),
148 : getegid(),
149 : &mdscli_ctx->mdscmd_unknown1.status,
150 : &mdscli_ctx->flags,
151 : &mdscli_ctx->mdscmd_unknown1.unkn7);
152 10 : if (tevent_req_nomem(subreq, req)) {
153 0 : return;
154 : }
155 10 : tevent_req_set_callback(subreq, mdscli_connect_unknown1_done, req);
156 : }
157 :
158 10 : static void mdscli_connect_unknown1_done(struct tevent_req *subreq)
159 : {
160 10 : struct tevent_req *req = tevent_req_callback_data(
161 : subreq, struct tevent_req);
162 10 : struct mdscli_connect_state *state = tevent_req_data(
163 : req, struct mdscli_connect_state);
164 10 : struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
165 : struct mdssvc_blob request_blob;
166 : NTSTATUS status;
167 :
168 10 : status = dcerpc_mdssvc_unknown1_recv(subreq, state);
169 10 : TALLOC_FREE(subreq);
170 10 : if (tevent_req_nterror(req, status)) {
171 0 : return;
172 : }
173 :
174 10 : status = mdscli_blob_fetch_props(state,
175 : state->mdscli_ctx,
176 : &request_blob);
177 10 : if (tevent_req_nterror(req, status)) {
178 0 : return;
179 : }
180 :
181 10 : subreq = dcerpc_mdssvc_cmd_send(state,
182 : state->ev,
183 : mdscli_ctx->bh,
184 : &mdscli_ctx->ph,
185 : 0,
186 : mdscli_ctx->dev,
187 : mdscli_ctx->mdscmd_open.unkn2,
188 : 0,
189 : mdscli_ctx->flags,
190 : request_blob,
191 : 0,
192 10 : mdscli_ctx->max_fragment_size,
193 : 1,
194 10 : mdscli_ctx->max_fragment_size,
195 : 0,
196 : 0,
197 : &mdscli_ctx->mdscmd_cmd.fragment,
198 : &state->response_blob,
199 : &mdscli_ctx->mdscmd_cmd.unkn9);
200 10 : if (tevent_req_nomem(subreq, req)) {
201 0 : return;
202 : }
203 10 : tevent_req_set_callback(subreq, mdscli_connect_fetch_props_done, req);
204 10 : mdscli_ctx->async_pending++;
205 10 : return;
206 : }
207 :
208 10 : static void mdscli_connect_fetch_props_done(struct tevent_req *subreq)
209 : {
210 10 : struct tevent_req *req = tevent_req_callback_data(
211 : subreq, struct tevent_req);
212 10 : struct mdscli_connect_state *state = tevent_req_data(
213 : req, struct mdscli_connect_state);
214 10 : struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
215 10 : DALLOC_CTX *d = NULL;
216 10 : sl_array_t *path_scope_array = NULL;
217 10 : char *path_scope = NULL;
218 : NTSTATUS status;
219 : bool ok;
220 :
221 10 : status = dcerpc_mdssvc_cmd_recv(subreq, state);
222 10 : TALLOC_FREE(subreq);
223 10 : state->mdscli_ctx->async_pending--;
224 10 : if (tevent_req_nterror(req, status)) {
225 0 : return;
226 : }
227 :
228 10 : d = dalloc_new(state);
229 10 : if (tevent_req_nomem(d, req)) {
230 0 : return;
231 : }
232 :
233 10 : ok = sl_unpack(d,
234 10 : (char *)state->response_blob.spotlight_blob,
235 10 : state->response_blob.length);
236 10 : if (!ok) {
237 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
238 0 : return;
239 : }
240 :
241 10 : path_scope_array = dalloc_value_for_key(d,
242 : "DALLOC_CTX", 0,
243 : "kMDSStorePathScopes",
244 : "sl_array_t");
245 10 : if (path_scope_array == NULL) {
246 0 : DBG_ERR("Missing kMDSStorePathScopes\n");
247 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
248 0 : return;
249 : }
250 :
251 10 : path_scope = dalloc_get(path_scope_array, "char *", 0);
252 10 : if (path_scope == NULL) {
253 0 : DBG_ERR("Missing path in kMDSStorePathScopes\n");
254 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
255 0 : return;
256 : }
257 :
258 10 : mdscli_ctx->path_scope_len = strlen(path_scope);
259 10 : if (mdscli_ctx->path_scope_len < 1 ||
260 10 : mdscli_ctx->path_scope_len > UINT16_MAX)
261 : {
262 0 : DBG_ERR("Bad path_scope: %s\n", path_scope);
263 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
264 0 : return;
265 : }
266 10 : mdscli_ctx->path_scope = talloc_strdup(mdscli_ctx, path_scope);
267 10 : if (tevent_req_nomem(mdscli_ctx->path_scope, req)) {
268 0 : return;
269 : }
270 :
271 10 : if (mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] == '/') {
272 10 : mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] = '\0';
273 10 : mdscli_ctx->path_scope_len--;
274 : }
275 :
276 10 : tevent_req_done(req);
277 : }
278 :
279 10 : NTSTATUS mdscli_connect_recv(struct tevent_req *req,
280 : TALLOC_CTX *mem_ctx,
281 : struct mdscli_ctx **mdscli_ctx)
282 : {
283 10 : struct mdscli_connect_state *state = tevent_req_data(
284 : req, struct mdscli_connect_state);
285 : NTSTATUS status;
286 :
287 10 : if (tevent_req_is_nterror(req, &status)) {
288 0 : tevent_req_received(req);
289 0 : return status;
290 : }
291 :
292 10 : *mdscli_ctx = talloc_move(mem_ctx, &state->mdscli_ctx);
293 10 : tevent_req_received(req);
294 10 : return NT_STATUS_OK;
295 : }
296 :
297 2 : NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
298 : struct dcerpc_binding_handle *bh,
299 : const char *share_name,
300 : const char *mount_path,
301 : struct mdscli_ctx **mdscli_ctx)
302 : {
303 2 : TALLOC_CTX *frame = talloc_stackframe();
304 2 : struct tevent_req *req = NULL;
305 2 : struct tevent_context *ev = NULL;
306 2 : NTSTATUS status = NT_STATUS_NO_MEMORY;
307 :
308 2 : ev = samba_tevent_context_init(frame);
309 2 : if (ev == NULL) {
310 0 : goto fail;
311 : }
312 :
313 2 : req = mdscli_connect_send(frame,
314 : ev,
315 : bh,
316 : share_name,
317 : mount_path);
318 2 : if (req == NULL) {
319 0 : goto fail;
320 : }
321 2 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
322 0 : goto fail;
323 : }
324 :
325 2 : status = mdscli_connect_recv(req, mem_ctx, mdscli_ctx);
326 2 : fail:
327 2 : TALLOC_FREE(frame);
328 2 : return status;
329 : }
330 :
331 : struct mdscli_search_state {
332 : struct mdscli_search_ctx *search;
333 : struct mdssvc_blob request_blob;
334 : struct mdssvc_blob response_blob;
335 : };
336 :
337 : static void mdscli_search_cmd_done(struct tevent_req *subreq);
338 :
339 6 : struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
340 : struct tevent_context *ev,
341 : struct mdscli_ctx *mdscli_ctx,
342 : const char *mds_query,
343 : const char *path_scope_in,
344 : bool live)
345 : {
346 6 : struct tevent_req *req = NULL;
347 6 : struct mdscli_search_state *state = NULL;
348 6 : struct tevent_req *subreq = NULL;
349 6 : struct mdscli_search_ctx *search = NULL;
350 6 : char *path_scope = NULL;
351 : NTSTATUS status;
352 :
353 6 : req = tevent_req_create(req, &state, struct mdscli_search_state);
354 6 : if (req == NULL) {
355 0 : return NULL;
356 : }
357 :
358 6 : search = talloc_zero(state, struct mdscli_search_ctx);
359 6 : if (tevent_req_nomem(search, req)) {
360 0 : return tevent_req_post(req, ev);
361 : }
362 :
363 6 : if (path_scope_in[0] == '/') {
364 6 : path_scope = talloc_strdup(search, path_scope_in);
365 : } else {
366 0 : path_scope = talloc_asprintf(search,
367 : "%s/%s",
368 0 : mdscli_ctx->mdscmd_open.share_path,
369 : path_scope_in);
370 : }
371 6 : if (tevent_req_nomem(path_scope, req)) {
372 0 : return tevent_req_post(req, ev);
373 : }
374 :
375 6 : *search = (struct mdscli_search_ctx) {
376 : .mdscli_ctx = mdscli_ctx,
377 6 : .ctx_id = mdscli_new_ctx_id(mdscli_ctx),
378 6 : .unique_id = generate_random_u64(),
379 : .live = live,
380 : .path_scope = path_scope,
381 6 : .mds_query = talloc_strdup(search, mds_query),
382 : };
383 6 : if (tevent_req_nomem(search->mds_query, req)) {
384 0 : return tevent_req_post(req, ev);
385 : }
386 :
387 6 : *state = (struct mdscli_search_state) {
388 : .search = search,
389 : };
390 :
391 6 : status = mdscli_blob_search(state,
392 : search,
393 6 : &state->request_blob);
394 6 : if (tevent_req_nterror(req, status)) {
395 0 : return tevent_req_post(req, ev);
396 : }
397 :
398 6 : subreq = dcerpc_mdssvc_cmd_send(state,
399 : ev,
400 : mdscli_ctx->bh,
401 : &mdscli_ctx->ph,
402 : 0,
403 : mdscli_ctx->dev,
404 : mdscli_ctx->mdscmd_open.unkn2,
405 : 0,
406 : mdscli_ctx->flags,
407 6 : state->request_blob,
408 : 0,
409 6 : mdscli_ctx->max_fragment_size,
410 : 1,
411 6 : mdscli_ctx->max_fragment_size,
412 : 0,
413 : 0,
414 : &mdscli_ctx->mdscmd_cmd.fragment,
415 6 : &state->response_blob,
416 : &mdscli_ctx->mdscmd_cmd.unkn9);
417 6 : if (tevent_req_nomem(subreq, req)) {
418 0 : return tevent_req_post(req, ev);
419 : }
420 6 : tevent_req_set_callback(subreq, mdscli_search_cmd_done, req);
421 6 : mdscli_ctx->async_pending++;
422 6 : return req;
423 : }
424 :
425 6 : static void mdscli_search_cmd_done(struct tevent_req *subreq)
426 : {
427 6 : struct tevent_req *req = tevent_req_callback_data(
428 : subreq, struct tevent_req);
429 6 : struct mdscli_search_state *state = tevent_req_data(
430 : req, struct mdscli_search_state);
431 6 : DALLOC_CTX *d = NULL;
432 6 : uint64_t *uint64p = NULL;
433 : NTSTATUS status;
434 : bool ok;
435 :
436 6 : status = dcerpc_mdssvc_cmd_recv(subreq, state);
437 6 : TALLOC_FREE(subreq);
438 6 : state->search->mdscli_ctx->async_pending--;
439 6 : if (tevent_req_nterror(req, status)) {
440 0 : return;
441 : }
442 :
443 6 : d = dalloc_new(state);
444 6 : if (tevent_req_nomem(d, req)) {
445 0 : return;
446 : }
447 :
448 6 : ok = sl_unpack(d,
449 6 : (char *)state->response_blob.spotlight_blob,
450 6 : state->response_blob.length);
451 6 : if (!ok) {
452 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
453 0 : return;
454 : }
455 :
456 6 : uint64p = dalloc_get(d,
457 : "DALLOC_CTX", 0,
458 : "uint64_t", 0);
459 6 : if (uint64p == NULL) {
460 0 : DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
461 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
462 0 : return;
463 : }
464 :
465 6 : if (*uint64p != 0) {
466 0 : DBG_DEBUG("Unexpected mds result: 0x%" PRIx64 "\n", *uint64p);
467 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
468 0 : return;
469 : }
470 :
471 6 : tevent_req_done(req);
472 6 : return;
473 : }
474 :
475 6 : NTSTATUS mdscli_search_recv(struct tevent_req *req,
476 : TALLOC_CTX *mem_ctx,
477 : struct mdscli_search_ctx **search)
478 : {
479 6 : struct mdscli_search_state *state = tevent_req_data(
480 : req, struct mdscli_search_state);
481 : NTSTATUS status;
482 :
483 6 : if (tevent_req_is_nterror(req, &status)) {
484 0 : tevent_req_received(req);
485 0 : return status;
486 : }
487 :
488 6 : *search = talloc_move(mem_ctx, &state->search);
489 6 : tevent_req_received(req);
490 6 : return NT_STATUS_OK;
491 : }
492 :
493 2 : NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
494 : struct mdscli_ctx *mdscli_ctx,
495 : const char *mds_query,
496 : const char *path_scope,
497 : bool live,
498 : struct mdscli_search_ctx **search)
499 : {
500 2 : TALLOC_CTX *frame = talloc_stackframe();
501 2 : struct tevent_req *req = NULL;
502 2 : struct tevent_context *ev = NULL;
503 2 : NTSTATUS status = NT_STATUS_NO_MEMORY;
504 :
505 2 : if (mdscli_ctx->async_pending != 0) {
506 0 : status = NT_STATUS_INVALID_PARAMETER;
507 0 : goto fail;
508 : }
509 :
510 2 : ev = samba_tevent_context_init(frame);
511 2 : if (ev == NULL) {
512 0 : goto fail;
513 : }
514 :
515 2 : req = mdscli_search_send(frame,
516 : ev,
517 : mdscli_ctx,
518 : mds_query,
519 : path_scope,
520 : live);
521 2 : if (req == NULL) {
522 0 : goto fail;
523 : }
524 2 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
525 0 : goto fail;
526 : }
527 :
528 2 : status = mdscli_search_recv(req, mem_ctx, search);
529 2 : fail:
530 2 : TALLOC_FREE(frame);
531 2 : return status;
532 : }
533 :
534 : struct mdscli_get_results_state {
535 : struct tevent_context *ev;
536 : struct mdscli_search_ctx *search;
537 : struct mdssvc_blob request_blob;
538 : struct mdssvc_blob response_fragment;
539 : DATA_BLOB response_data;
540 : uint64_t *cnids;
541 : uint32_t fragment;
542 : };
543 :
544 : static void mdscli_get_results_cmd_done(struct tevent_req *subreq);
545 :
546 8 : struct tevent_req *mdscli_get_results_send(
547 : TALLOC_CTX *mem_ctx,
548 : struct tevent_context *ev,
549 : struct mdscli_search_ctx *search)
550 : {
551 8 : struct tevent_req *req = NULL;
552 8 : struct mdscli_get_results_state *state = NULL;
553 8 : struct tevent_req *subreq = NULL;
554 8 : struct mdscli_ctx *mdscli_ctx = search->mdscli_ctx;
555 : NTSTATUS status;
556 :
557 8 : req = tevent_req_create(req, &state, struct mdscli_get_results_state);
558 8 : if (req == NULL) {
559 0 : return NULL;
560 : }
561 :
562 8 : *state = (struct mdscli_get_results_state) {
563 : .ev = ev,
564 : .search = search,
565 : };
566 :
567 8 : status = mdscli_blob_get_results(state,
568 : search,
569 8 : &state->request_blob);
570 8 : if (tevent_req_nterror(req, status)) {
571 0 : return tevent_req_post(req, ev);
572 : }
573 :
574 8 : subreq = dcerpc_mdssvc_cmd_send(state,
575 : ev,
576 : mdscli_ctx->bh,
577 : &mdscli_ctx->ph,
578 : 0,
579 : mdscli_ctx->dev,
580 : mdscli_ctx->mdscmd_open.unkn2,
581 : 0,
582 : mdscli_ctx->flags,
583 8 : state->request_blob,
584 : 0,
585 8 : mdscli_ctx->max_fragment_size,
586 : 1,
587 8 : mdscli_ctx->max_fragment_size,
588 : 0,
589 : 0,
590 8 : &state->fragment,
591 8 : &state->response_fragment,
592 : &mdscli_ctx->mdscmd_cmd.unkn9);
593 8 : if (tevent_req_nomem(subreq, req)) {
594 0 : return tevent_req_post(req, ev);
595 : }
596 8 : tevent_req_set_callback(subreq, mdscli_get_results_cmd_done, req);
597 8 : mdscli_ctx->async_pending++;
598 8 : return req;
599 : }
600 :
601 8 : static void mdscli_get_results_cmd_done(struct tevent_req *subreq)
602 : {
603 8 : struct tevent_req *req = tevent_req_callback_data(
604 : subreq, struct tevent_req);
605 8 : struct mdscli_get_results_state *state = tevent_req_data(
606 : req, struct mdscli_get_results_state);
607 8 : struct mdscli_ctx *mdscli_ctx = state->search->mdscli_ctx;
608 : size_t oldsize, newsize;
609 8 : DALLOC_CTX *d = NULL;
610 8 : uint64_t *uint64p = NULL;
611 8 : bool search_in_progress = false;
612 8 : sl_cnids_t *cnids = NULL;
613 : size_t ncnids;
614 : size_t i;
615 : NTSTATUS status;
616 : bool ok;
617 :
618 8 : status = dcerpc_mdssvc_cmd_recv(subreq, state);
619 8 : TALLOC_FREE(subreq);
620 8 : state->search->mdscli_ctx->async_pending--;
621 8 : if (tevent_req_nterror(req, status)) {
622 0 : return;
623 : }
624 :
625 8 : oldsize = state->response_data.length;
626 8 : newsize = oldsize + state->response_fragment.length;
627 8 : if (newsize < oldsize) {
628 0 : tevent_req_nterror(req, NT_STATUS_INTEGER_OVERFLOW);
629 0 : return;
630 : }
631 :
632 8 : ok = data_blob_realloc(state, &state->response_data, newsize);
633 8 : if (!ok) {
634 0 : tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
635 0 : return;
636 : }
637 8 : (void)memcpy(state->response_data.data + oldsize,
638 8 : state->response_fragment.spotlight_blob,
639 8 : state->response_fragment.length);
640 :
641 8 : TALLOC_FREE(state->response_fragment.spotlight_blob);
642 8 : state->response_fragment.length = 0;
643 8 : state->response_fragment.size = 0;
644 :
645 8 : if (state->fragment != 0) {
646 0 : subreq = dcerpc_mdssvc_cmd_send(
647 : state,
648 : state->ev,
649 : mdscli_ctx->bh,
650 : &mdscli_ctx->ph,
651 : 0,
652 : mdscli_ctx->dev,
653 : mdscli_ctx->mdscmd_open.unkn2,
654 : 1,
655 : mdscli_ctx->flags,
656 : state->request_blob,
657 : 0,
658 0 : mdscli_ctx->max_fragment_size,
659 : 1,
660 0 : mdscli_ctx->max_fragment_size,
661 : 0,
662 : 0,
663 : &state->fragment,
664 : &state->response_fragment,
665 : &mdscli_ctx->mdscmd_cmd.unkn9);
666 0 : if (tevent_req_nomem(subreq, req)) {
667 0 : tevent_req_post(req, state->ev);
668 0 : return;
669 : }
670 0 : tevent_req_set_callback(subreq,
671 : mdscli_get_results_cmd_done,
672 : req);
673 0 : mdscli_ctx->async_pending++;
674 0 : return;
675 : }
676 :
677 8 : d = dalloc_new(state);
678 8 : if (tevent_req_nomem(d, req)) {
679 0 : return;
680 : }
681 :
682 8 : ok = sl_unpack(d,
683 8 : (char *)state->response_data.data,
684 : state->response_data.length);
685 8 : if (!ok) {
686 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
687 0 : return;
688 : }
689 :
690 8 : uint64p = dalloc_get(d,
691 : "DALLOC_CTX", 0,
692 : "uint64_t", 0);
693 8 : if (uint64p == NULL) {
694 0 : DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
695 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
696 0 : return;
697 : }
698 :
699 8 : if (*uint64p == 35) {
700 0 : DBG_DEBUG("Search in progress\n");
701 0 : search_in_progress = true;
702 : }
703 :
704 8 : cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1);
705 8 : if (cnids == NULL) {
706 0 : DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
707 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
708 0 : return;
709 : }
710 :
711 8 : ncnids = dalloc_size(cnids->ca_cnids);
712 8 : if (ncnids == 0 && !search_in_progress) {
713 2 : tevent_req_nterror(req, NT_STATUS_NO_MORE_MATCHES);
714 2 : return;
715 : }
716 :
717 6 : if (cnids->ca_unkn1 != 0xadd) {
718 : /*
719 : * Whatever 0xadd means... but it seems to be the standard value
720 : * macOS mdssvc returns here.
721 : */
722 0 : DBG_DEBUG("unexpected ca_unkn1: %s", dalloc_dump(d, 0));
723 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
724 0 : return;
725 : }
726 :
727 6 : if (cnids->ca_context != state->search->ctx_id.connection ) {
728 0 : DBG_DEBUG("unexpected ca_context: %s", dalloc_dump(d, 0));
729 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
730 0 : return;
731 : }
732 :
733 6 : state->cnids = talloc_zero_array(state, uint64_t, ncnids);
734 6 : if (tevent_req_nomem(state->cnids, req)) {
735 0 : return;
736 : }
737 :
738 32 : for (i = 0; i < ncnids; i++) {
739 26 : uint64_t *cnid = NULL;
740 :
741 26 : cnid = dalloc_get(cnids->ca_cnids, "uint64_t", i);
742 26 : if (cnid == NULL) {
743 0 : DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
744 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
745 0 : return;
746 : }
747 26 : state->cnids[i] = *cnid;
748 : }
749 :
750 6 : tevent_req_done(req);
751 6 : return;
752 : }
753 :
754 8 : NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
755 : TALLOC_CTX *mem_ctx,
756 : uint64_t **cnids)
757 : {
758 8 : struct mdscli_get_results_state *state = tevent_req_data(
759 : req, struct mdscli_get_results_state);
760 : NTSTATUS status;
761 :
762 8 : if (tevent_req_is_nterror(req, &status)) {
763 2 : tevent_req_received(req);
764 2 : return status;
765 : }
766 :
767 6 : *cnids = talloc_move(mem_ctx, &state->cnids);
768 :
769 6 : tevent_req_received(req);
770 6 : return NT_STATUS_OK;
771 : }
772 :
773 4 : NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
774 : struct mdscli_search_ctx *search,
775 : uint64_t **_cnids)
776 : {
777 4 : TALLOC_CTX *frame = talloc_stackframe();
778 4 : struct tevent_req *req = NULL;
779 4 : struct tevent_context *ev = NULL;
780 4 : NTSTATUS status = NT_STATUS_NO_MEMORY;
781 :
782 4 : if (search->mdscli_ctx->async_pending != 0) {
783 0 : status = NT_STATUS_INVALID_PARAMETER;
784 0 : goto fail;
785 : }
786 :
787 4 : ev = samba_tevent_context_init(frame);
788 4 : if (ev == NULL) {
789 0 : goto fail;
790 : }
791 :
792 4 : req = mdscli_get_results_send(frame,
793 : ev,
794 : search);
795 4 : if (req == NULL) {
796 0 : goto fail;
797 : }
798 4 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
799 0 : goto fail;
800 : }
801 :
802 4 : status = mdscli_get_results_recv(req, mem_ctx, _cnids);
803 4 : fail:
804 4 : TALLOC_FREE(frame);
805 4 : return status;
806 : }
807 :
808 : struct mdscli_get_path_state {
809 : struct mdscli_ctx *mdscli_ctx;
810 : struct mdssvc_blob request_blob;
811 : struct mdssvc_blob response_blob;
812 : char *path;
813 : };
814 :
815 : static void mdscli_get_path_done(struct tevent_req *subreq);
816 :
817 26 : struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
818 : struct tevent_context *ev,
819 : struct mdscli_ctx *mdscli_ctx,
820 : uint64_t cnid)
821 : {
822 26 : struct tevent_req *req = NULL;
823 26 : struct mdscli_get_path_state *state = NULL;
824 26 : struct tevent_req *subreq = NULL;
825 : NTSTATUS status;
826 :
827 26 : req = tevent_req_create(req, &state, struct mdscli_get_path_state);
828 26 : if (req == NULL) {
829 0 : return NULL;
830 : }
831 26 : *state = (struct mdscli_get_path_state) {
832 : .mdscli_ctx = mdscli_ctx,
833 : };
834 :
835 26 : status = mdscli_blob_get_path(state,
836 : mdscli_ctx,
837 : cnid,
838 26 : &state->request_blob);
839 26 : if (tevent_req_nterror(req, status)) {
840 0 : return tevent_req_post(req, ev);
841 : }
842 :
843 26 : subreq = dcerpc_mdssvc_cmd_send(state,
844 : ev,
845 : mdscli_ctx->bh,
846 : &mdscli_ctx->ph,
847 : 0,
848 : mdscli_ctx->dev,
849 : mdscli_ctx->mdscmd_open.unkn2,
850 : 0,
851 : mdscli_ctx->flags,
852 26 : state->request_blob,
853 : 0,
854 26 : mdscli_ctx->max_fragment_size,
855 : 1,
856 26 : mdscli_ctx->max_fragment_size,
857 : 0,
858 : 0,
859 : &mdscli_ctx->mdscmd_cmd.fragment,
860 26 : &state->response_blob,
861 : &mdscli_ctx->mdscmd_cmd.unkn9);
862 26 : if (tevent_req_nomem(subreq, req)) {
863 0 : return tevent_req_post(req, ev);
864 : }
865 26 : tevent_req_set_callback(subreq, mdscli_get_path_done, req);
866 26 : mdscli_ctx->async_pending++;
867 26 : return req;
868 : }
869 :
870 26 : static void mdscli_get_path_done(struct tevent_req *subreq)
871 : {
872 26 : struct tevent_req *req = tevent_req_callback_data(
873 : subreq, struct tevent_req);
874 26 : struct mdscli_get_path_state *state = tevent_req_data(
875 : req, struct mdscli_get_path_state);
876 26 : DALLOC_CTX *d = NULL;
877 : size_t pathlen;
878 : size_t prefixlen;
879 26 : char *path = NULL;
880 26 : const char *p = NULL;
881 : NTSTATUS status;
882 : bool ok;
883 :
884 26 : status = dcerpc_mdssvc_cmd_recv(subreq, state);
885 26 : TALLOC_FREE(subreq);
886 26 : state->mdscli_ctx->async_pending--;
887 26 : if (tevent_req_nterror(req, status)) {
888 0 : return;
889 : }
890 :
891 26 : d = dalloc_new(state);
892 26 : if (tevent_req_nomem(d, req)) {
893 0 : return;
894 : }
895 :
896 26 : ok = sl_unpack(d,
897 26 : (char *)state->response_blob.spotlight_blob,
898 26 : state->response_blob.length);
899 26 : if (!ok) {
900 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
901 0 : return;
902 : }
903 :
904 26 : path = dalloc_get(d,
905 : "DALLOC_CTX", 0,
906 : "DALLOC_CTX", 2,
907 : "DALLOC_CTX", 0,
908 : "DALLOC_CTX", 1,
909 : "char *", 0);
910 26 : if (path == NULL) {
911 0 : DBG_DEBUG("No path in mds response: %s", dalloc_dump(d, 0));
912 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
913 0 : return;
914 : }
915 :
916 : /* Path is prefixed by /PATHSCOPE/SHARENAME/, strip it */
917 26 : pathlen = strlen(path);
918 :
919 : /*
920 : * path_scope_len and share_path_len are already checked to be smaller
921 : * then UINT16_MAX so this can't overflow
922 : */
923 26 : prefixlen = state->mdscli_ctx->path_scope_len
924 26 : + state->mdscli_ctx->mdscmd_open.share_path_len;
925 :
926 26 : if (pathlen < prefixlen) {
927 0 : DBG_DEBUG("Bad path: %s\n", path);
928 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
929 0 : return;
930 : }
931 :
932 26 : p = path + prefixlen;
933 52 : while (*p == '/') {
934 26 : p++;
935 : }
936 26 : if (*p == '\0') {
937 0 : DBG_DEBUG("Bad path: %s\n", path);
938 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
939 0 : return;
940 : }
941 :
942 26 : state->path = talloc_strdup(state, p);
943 26 : if (state->path == NULL) {
944 0 : tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
945 0 : return;
946 : }
947 26 : DBG_DEBUG("path: %s\n", state->path);
948 :
949 26 : tevent_req_done(req);
950 26 : return;
951 : }
952 :
953 26 : NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
954 : TALLOC_CTX *mem_ctx,
955 : char **path)
956 : {
957 26 : struct mdscli_get_path_state *state = tevent_req_data(
958 : req, struct mdscli_get_path_state);
959 : NTSTATUS status;
960 :
961 26 : if (tevent_req_is_nterror(req, &status)) {
962 0 : tevent_req_received(req);
963 0 : return status;
964 : }
965 :
966 26 : *path = talloc_move(mem_ctx, &state->path);
967 26 : tevent_req_received(req);
968 26 : return NT_STATUS_OK;
969 : }
970 :
971 4 : NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
972 : struct mdscli_ctx *mdscli_ctx,
973 : uint64_t cnid,
974 : char **path)
975 : {
976 4 : TALLOC_CTX *frame = talloc_stackframe();
977 4 : struct tevent_req *req = NULL;
978 4 : struct tevent_context *ev = NULL;
979 4 : NTSTATUS status = NT_STATUS_NO_MEMORY;
980 :
981 4 : if (mdscli_ctx->async_pending != 0) {
982 0 : status = NT_STATUS_INVALID_PARAMETER;
983 0 : goto fail;
984 : }
985 :
986 4 : ev = samba_tevent_context_init(frame);
987 4 : if (ev == NULL) {
988 0 : goto fail;
989 : }
990 :
991 4 : req = mdscli_get_path_send(frame, ev, mdscli_ctx, cnid);
992 4 : if (req == NULL) {
993 0 : goto fail;
994 : }
995 4 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
996 0 : goto fail;
997 : }
998 :
999 4 : status = mdscli_get_path_recv(req, mem_ctx, path);
1000 4 : fail:
1001 4 : TALLOC_FREE(frame);
1002 4 : return status;
1003 : }
1004 :
1005 : struct mdscli_close_search_state {
1006 : struct mdscli_search_ctx *search;
1007 : struct mdssvc_blob request_blob;
1008 : struct mdssvc_blob response_blob;
1009 : };
1010 :
1011 : static void mdscli_close_search_done(struct tevent_req *subreq);
1012 :
1013 6 : struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
1014 : struct tevent_context *ev,
1015 : struct mdscli_search_ctx **search)
1016 : {
1017 6 : struct mdscli_ctx *mdscli_ctx = NULL;
1018 6 : struct tevent_req *req = NULL;
1019 6 : struct mdscli_close_search_state *state = NULL;
1020 6 : struct tevent_req *subreq = NULL;
1021 : NTSTATUS status;
1022 :
1023 6 : req = tevent_req_create(req, &state, struct mdscli_close_search_state);
1024 6 : if (req == NULL) {
1025 0 : return NULL;
1026 : }
1027 12 : *state = (struct mdscli_close_search_state) {
1028 6 : .search = talloc_move(state, search),
1029 : };
1030 6 : mdscli_ctx = state->search->mdscli_ctx;
1031 :
1032 6 : status = mdscli_blob_close_search(state,
1033 6 : state->search,
1034 6 : &state->request_blob);
1035 6 : if (tevent_req_nterror(req, status)) {
1036 0 : return tevent_req_post(req, ev);
1037 : }
1038 :
1039 6 : subreq = dcerpc_mdssvc_cmd_send(state,
1040 : ev,
1041 : mdscli_ctx->bh,
1042 : &mdscli_ctx->ph,
1043 : 0,
1044 : mdscli_ctx->dev,
1045 : mdscli_ctx->mdscmd_open.unkn2,
1046 : 0,
1047 : mdscli_ctx->flags,
1048 6 : state->request_blob,
1049 : 0,
1050 6 : mdscli_ctx->max_fragment_size,
1051 : 1,
1052 6 : mdscli_ctx->max_fragment_size,
1053 : 0,
1054 : 0,
1055 : &mdscli_ctx->mdscmd_cmd.fragment,
1056 6 : &state->response_blob,
1057 : &mdscli_ctx->mdscmd_cmd.unkn9);
1058 6 : if (tevent_req_nomem(subreq, req)) {
1059 0 : return tevent_req_post(req, ev);
1060 : }
1061 6 : tevent_req_set_callback(subreq, mdscli_close_search_done, req);
1062 6 : mdscli_ctx->async_pending++;
1063 6 : return req;
1064 : }
1065 :
1066 6 : static void mdscli_close_search_done(struct tevent_req *subreq)
1067 : {
1068 6 : struct tevent_req *req = tevent_req_callback_data(
1069 : subreq, struct tevent_req);
1070 6 : struct mdscli_close_search_state *state = tevent_req_data(
1071 : req, struct mdscli_close_search_state);
1072 : NTSTATUS status;
1073 :
1074 6 : status = dcerpc_mdssvc_cmd_recv(subreq, state);
1075 6 : TALLOC_FREE(subreq);
1076 6 : state->search->mdscli_ctx->async_pending--;
1077 6 : if (tevent_req_nterror(req, status)) {
1078 0 : return;
1079 : }
1080 :
1081 6 : tevent_req_done(req);
1082 6 : return;
1083 : }
1084 :
1085 6 : NTSTATUS mdscli_close_search_recv(struct tevent_req *req)
1086 : {
1087 6 : return tevent_req_simple_recv_ntstatus(req);
1088 : }
1089 :
1090 2 : NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search)
1091 : {
1092 2 : TALLOC_CTX *frame = talloc_stackframe();
1093 2 : struct tevent_req *req = NULL;
1094 2 : struct tevent_context *ev = NULL;
1095 2 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1096 :
1097 2 : if ((*search)->mdscli_ctx->async_pending != 0) {
1098 0 : status = NT_STATUS_INVALID_PARAMETER;
1099 0 : goto fail;
1100 : }
1101 :
1102 2 : ev = samba_tevent_context_init(frame);
1103 2 : if (ev == NULL) {
1104 0 : goto fail;
1105 : }
1106 :
1107 2 : req = mdscli_close_search_send(frame, ev, search);
1108 2 : if (req == NULL) {
1109 0 : goto fail;
1110 : }
1111 2 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1112 0 : goto fail;
1113 : }
1114 :
1115 2 : status = mdscli_close_search_recv(req);
1116 2 : fail:
1117 2 : TALLOC_FREE(frame);
1118 2 : return status;
1119 : }
1120 :
1121 : struct mdscli_disconnect_state {
1122 : struct mdscli_ctx *mdscli_ctx;
1123 : };
1124 :
1125 : static void mdscli_disconnect_done(struct tevent_req *subreq);
1126 :
1127 10 : struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
1128 : struct tevent_context *ev,
1129 : struct mdscli_ctx *mdscli_ctx)
1130 : {
1131 10 : struct tevent_req *req = NULL;
1132 10 : struct mdscli_disconnect_state *state = NULL;
1133 10 : struct tevent_req *subreq = NULL;
1134 :
1135 10 : req = tevent_req_create(req, &state, struct mdscli_disconnect_state);
1136 10 : if (req == NULL) {
1137 0 : return NULL;
1138 : }
1139 10 : *state = (struct mdscli_disconnect_state) {
1140 : .mdscli_ctx = mdscli_ctx,
1141 : };
1142 :
1143 10 : subreq = dcerpc_mdssvc_close_send(state,
1144 : ev,
1145 : mdscli_ctx->bh,
1146 : &mdscli_ctx->ph,
1147 : 0,
1148 : mdscli_ctx->dev,
1149 : mdscli_ctx->mdscmd_open.unkn2,
1150 : 0,
1151 : &mdscli_ctx->ph,
1152 : &mdscli_ctx->mdscmd_close.status);
1153 10 : if (tevent_req_nomem(subreq, req)) {
1154 0 : return tevent_req_post(req, ev);
1155 : }
1156 10 : tevent_req_set_callback(subreq, mdscli_disconnect_done, req);
1157 10 : mdscli_ctx->async_pending++;
1158 10 : return req;
1159 : }
1160 :
1161 10 : static void mdscli_disconnect_done(struct tevent_req *subreq)
1162 : {
1163 10 : struct tevent_req *req = tevent_req_callback_data(
1164 : subreq, struct tevent_req);
1165 10 : struct mdscli_disconnect_state *state = tevent_req_data(
1166 : req, struct mdscli_disconnect_state);
1167 : NTSTATUS status;
1168 :
1169 10 : status = dcerpc_mdssvc_close_recv(subreq, state);
1170 10 : TALLOC_FREE(subreq);
1171 10 : state->mdscli_ctx->async_pending--;
1172 10 : if (tevent_req_nterror(req, status)) {
1173 0 : return;
1174 : }
1175 :
1176 10 : tevent_req_done(req);
1177 10 : return;
1178 : }
1179 :
1180 10 : NTSTATUS mdscli_disconnect_recv(struct tevent_req *req)
1181 : {
1182 10 : return tevent_req_simple_recv_ntstatus(req);
1183 : }
1184 :
1185 2 : NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx)
1186 : {
1187 2 : TALLOC_CTX *frame = talloc_stackframe();
1188 2 : struct tevent_req *req = NULL;
1189 2 : struct tevent_context *ev = NULL;
1190 2 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1191 :
1192 2 : if (mdscli_ctx->async_pending != 0) {
1193 0 : status = NT_STATUS_INVALID_PARAMETER;
1194 0 : goto fail;
1195 : }
1196 :
1197 2 : ev = samba_tevent_context_init(frame);
1198 2 : if (ev == NULL) {
1199 0 : goto fail;
1200 : }
1201 :
1202 2 : req = mdscli_disconnect_send(frame, ev, mdscli_ctx);
1203 2 : if (req == NULL) {
1204 0 : goto fail;
1205 : }
1206 2 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1207 0 : goto fail;
1208 : }
1209 :
1210 2 : status = mdscli_disconnect_recv(req);
1211 2 : fail:
1212 2 : TALLOC_FREE(frame);
1213 2 : return status;
1214 : }
|