auth/credentials: don't ignore "client use kerberos" and --use-kerberos for machine...
[samba.git] / source3 / rpc_client / cli_mdssvc.c
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 struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx)
32 {
33         mdscli_ctx->ctx_id.id++;
34         return mdscli_ctx->ctx_id;
35 }
36
37 char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
38                           struct mdscli_ctx *mdscli_ctx)
39 {
40         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 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         struct tevent_req *req = NULL;
60         struct mdscli_connect_state *state = NULL;
61         struct tevent_req *subreq = NULL;
62         struct mdscli_ctx *ctx = NULL;
63
64         req = tevent_req_create(req, &state, struct mdscli_connect_state);
65         if (req == NULL) {
66                 return NULL;
67         }
68
69         ctx = talloc_zero(state, struct mdscli_ctx);
70         if (tevent_req_nomem(ctx, req)) {
71                 return tevent_req_post(req, ev);
72         }
73
74         *state = (struct mdscli_connect_state) {
75                 .ev = ev,
76                 .mdscli_ctx = ctx,
77         };
78
79         *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         subreq = dcerpc_mdssvc_open_send(state,
91                                          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                                          ctx->mdscmd_open.share_path,
99                                          &ctx->ph);
100         if (tevent_req_nomem(subreq, req)) {
101                 return tevent_req_post(req, state->ev);
102         }
103         tevent_req_set_callback(subreq, mdscli_connect_open_done, req);
104         ctx->async_pending++;
105
106         return req;
107 }
108
109 static void mdscli_connect_open_done(struct tevent_req *subreq)
110 {
111         struct tevent_req *req = tevent_req_callback_data(
112                 subreq, struct tevent_req);
113         struct mdscli_connect_state *state = tevent_req_data(
114                 req, struct mdscli_connect_state);
115         struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
116         size_t share_path_len;
117         NTSTATUS status;
118
119         status = dcerpc_mdssvc_open_recv(subreq, state);
120         TALLOC_FREE(subreq);
121         state->mdscli_ctx->async_pending--;
122         if (tevent_req_nterror(req, status)) {
123                 return;
124         }
125
126         share_path_len = strlen(mdscli_ctx->mdscmd_open.share_path);
127         if (share_path_len < 1 || share_path_len > UINT16_MAX) {
128                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
129                 return;
130         }
131         mdscli_ctx->mdscmd_open.share_path_len = share_path_len;
132
133         if (mdscli_ctx->mdscmd_open.share_path[share_path_len-1] == '/') {
134                 mdscli_ctx->mdscmd_open.share_path[share_path_len-1] = '\0';
135                 mdscli_ctx->mdscmd_open.share_path_len--;
136         }
137
138         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         if (tevent_req_nomem(subreq, req)) {
153                 return;
154         }
155         tevent_req_set_callback(subreq, mdscli_connect_unknown1_done, req);
156 }
157
158 static void mdscli_connect_unknown1_done(struct tevent_req *subreq)
159 {
160         struct tevent_req *req = tevent_req_callback_data(
161                 subreq, struct tevent_req);
162         struct mdscli_connect_state *state = tevent_req_data(
163                 req, struct mdscli_connect_state);
164         struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
165         struct mdssvc_blob request_blob;
166         NTSTATUS status;
167
168         status = dcerpc_mdssvc_unknown1_recv(subreq, state);
169         TALLOC_FREE(subreq);
170         if (tevent_req_nterror(req, status)) {
171                 return;
172         }
173
174         status = mdscli_blob_fetch_props(state,
175                                          state->mdscli_ctx,
176                                          &request_blob);
177         if (tevent_req_nterror(req, status)) {
178                 return;
179         }
180
181         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                                         mdscli_ctx->max_fragment_size,
193                                         1,
194                                         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         if (tevent_req_nomem(subreq, req)) {
201                 return;
202         }
203         tevent_req_set_callback(subreq, mdscli_connect_fetch_props_done, req);
204         mdscli_ctx->async_pending++;
205         return;
206 }
207
208 static void mdscli_connect_fetch_props_done(struct tevent_req *subreq)
209 {
210         struct tevent_req *req = tevent_req_callback_data(
211                 subreq, struct tevent_req);
212         struct mdscli_connect_state *state = tevent_req_data(
213                 req, struct mdscli_connect_state);
214         struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
215         DALLOC_CTX *d = NULL;
216         sl_array_t *path_scope_array = NULL;
217         char *path_scope = NULL;
218         NTSTATUS status;
219         bool ok;
220
221         status = dcerpc_mdssvc_cmd_recv(subreq, state);
222         TALLOC_FREE(subreq);
223         state->mdscli_ctx->async_pending--;
224         if (tevent_req_nterror(req, status)) {
225                 return;
226         }
227
228         d = dalloc_new(state);
229         if (tevent_req_nomem(d, req)) {
230                 return;
231         }
232
233         ok = sl_unpack(d,
234                        (char *)state->response_blob.spotlight_blob,
235                        state->response_blob.length);
236         if (!ok) {
237                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
238                 return;
239         }
240
241         path_scope_array = dalloc_value_for_key(d,
242                                                 "DALLOC_CTX", 0,
243                                                 "kMDSStorePathScopes",
244                                                 "sl_array_t");
245         if (path_scope_array == NULL) {
246                 DBG_ERR("Missing kMDSStorePathScopes\n");
247                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
248                 return;
249         }
250
251         path_scope = dalloc_get(path_scope_array, "char *", 0);
252         if (path_scope == NULL) {
253                 DBG_ERR("Missing path in kMDSStorePathScopes\n");
254                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
255                 return;
256         }
257
258         mdscli_ctx->path_scope_len = strlen(path_scope);
259         if (mdscli_ctx->path_scope_len < 1 ||
260             mdscli_ctx->path_scope_len > UINT16_MAX)
261         {
262                 DBG_ERR("Bad path_scope: %s\n", path_scope);
263                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
264                 return;
265         }
266         mdscli_ctx->path_scope = talloc_strdup(mdscli_ctx, path_scope);
267         if (tevent_req_nomem(mdscli_ctx->path_scope, req)) {
268                 return;
269         }
270
271         if (mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] == '/') {
272                 mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] = '\0';
273                 mdscli_ctx->path_scope_len--;
274         }
275
276         tevent_req_done(req);
277 }
278
279 NTSTATUS mdscli_connect_recv(struct tevent_req *req,
280                              TALLOC_CTX *mem_ctx,
281                              struct mdscli_ctx **mdscli_ctx)
282 {
283         struct mdscli_connect_state *state = tevent_req_data(
284                 req, struct mdscli_connect_state);
285         NTSTATUS status;
286
287         if (tevent_req_is_nterror(req, &status)) {
288                 tevent_req_received(req);
289                 return status;
290         }
291
292         *mdscli_ctx = talloc_move(mem_ctx, &state->mdscli_ctx);
293         tevent_req_received(req);
294         return NT_STATUS_OK;
295 }
296
297 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         TALLOC_CTX *frame = talloc_stackframe();
304         struct tevent_req *req = NULL;
305         struct tevent_context *ev = NULL;
306         NTSTATUS status = NT_STATUS_NO_MEMORY;
307
308         ev = samba_tevent_context_init(frame);
309         if (ev == NULL) {
310                 goto fail;
311         }
312
313         req = mdscli_connect_send(frame,
314                                   ev,
315                                   bh,
316                                   share_name,
317                                   mount_path);
318         if (req == NULL) {
319                 goto fail;
320         }
321         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
322                 goto fail;
323         }
324
325         status = mdscli_connect_recv(req, mem_ctx, mdscli_ctx);
326 fail:
327         TALLOC_FREE(frame);
328         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 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         struct tevent_req *req = NULL;
347         struct mdscli_search_state *state = NULL;
348         struct tevent_req *subreq = NULL;
349         struct mdscli_search_ctx *search = NULL;
350         char *path_scope = NULL;
351         NTSTATUS status;
352
353         req = tevent_req_create(req, &state, struct mdscli_search_state);
354         if (req == NULL) {
355                 return NULL;
356         }
357
358         search = talloc_zero(state, struct mdscli_search_ctx);
359         if (tevent_req_nomem(search, req)) {
360                 return tevent_req_post(req, ev);
361         }
362
363         if (path_scope_in[0] == '/') {
364                 path_scope = talloc_strdup(search, path_scope_in);
365         } else {
366                 path_scope = talloc_asprintf(search,
367                                              "%s/%s",
368                                              mdscli_ctx->mdscmd_open.share_path,
369                                              path_scope_in);
370         }
371         if (tevent_req_nomem(path_scope, req)) {
372                 return tevent_req_post(req, ev);
373         }
374
375         *search = (struct mdscli_search_ctx) {
376                 .mdscli_ctx = mdscli_ctx,
377                 .ctx_id = mdscli_new_ctx_id(mdscli_ctx),
378                 .unique_id = generate_random_u64(),
379                 .live = live,
380                 .path_scope = path_scope,
381                 .mds_query = talloc_strdup(search, mds_query),
382         };
383         if (tevent_req_nomem(search->mds_query, req)) {
384                 return tevent_req_post(req, ev);
385         }
386
387         *state = (struct mdscli_search_state) {
388                 .search = search,
389         };
390
391         status = mdscli_blob_search(state,
392                                     search,
393                                     &state->request_blob);
394         if (tevent_req_nterror(req, status)) {
395                 return tevent_req_post(req, ev);
396         }
397
398         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                                         state->request_blob,
408                                         0,
409                                         mdscli_ctx->max_fragment_size,
410                                         1,
411                                         mdscli_ctx->max_fragment_size,
412                                         0,
413                                         0,
414                                         &mdscli_ctx->mdscmd_cmd.fragment,
415                                         &state->response_blob,
416                                         &mdscli_ctx->mdscmd_cmd.unkn9);
417         if (tevent_req_nomem(subreq, req)) {
418                 return tevent_req_post(req, ev);
419         }
420         tevent_req_set_callback(subreq, mdscli_search_cmd_done, req);
421         mdscli_ctx->async_pending++;
422         return req;
423 }
424
425 static void mdscli_search_cmd_done(struct tevent_req *subreq)
426 {
427         struct tevent_req *req = tevent_req_callback_data(
428                 subreq, struct tevent_req);
429         struct mdscli_search_state *state = tevent_req_data(
430                 req, struct mdscli_search_state);
431         DALLOC_CTX *d = NULL;
432         uint64_t *uint64p = NULL;
433         NTSTATUS status;
434         bool ok;
435
436         status = dcerpc_mdssvc_cmd_recv(subreq, state);
437         TALLOC_FREE(subreq);
438         state->search->mdscli_ctx->async_pending--;
439         if (tevent_req_nterror(req, status)) {
440                 return;
441         }
442
443         d = dalloc_new(state);
444         if (tevent_req_nomem(d, req)) {
445                 return;
446         }
447
448         ok = sl_unpack(d,
449                        (char *)state->response_blob.spotlight_blob,
450                        state->response_blob.length);
451         if (!ok) {
452                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
453                 return;
454         }
455
456         uint64p = dalloc_get(d,
457                              "DALLOC_CTX", 0,
458                              "uint64_t", 0);
459         if (uint64p == NULL) {
460                 DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
461                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
462                 return;
463         }
464
465         if (*uint64p != 0) {
466                 DBG_DEBUG("Unexpected mds result: 0x%" PRIx64 "\n", *uint64p);
467                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
468                 return;
469         }
470
471         tevent_req_done(req);
472         return;
473 }
474
475 NTSTATUS mdscli_search_recv(struct tevent_req *req,
476                             TALLOC_CTX *mem_ctx,
477                             struct mdscli_search_ctx **search)
478 {
479         struct mdscli_search_state *state = tevent_req_data(
480                 req, struct mdscli_search_state);
481         NTSTATUS status;
482
483         if (tevent_req_is_nterror(req, &status)) {
484                 tevent_req_received(req);
485                 return status;
486         }
487
488         *search = talloc_move(mem_ctx, &state->search);
489         tevent_req_received(req);
490         return NT_STATUS_OK;
491 }
492
493 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         TALLOC_CTX *frame = talloc_stackframe();
501         struct tevent_req *req = NULL;
502         struct tevent_context *ev = NULL;
503         NTSTATUS status = NT_STATUS_NO_MEMORY;
504
505         if (mdscli_ctx->async_pending != 0) {
506                 status = NT_STATUS_INVALID_PARAMETER;
507                 goto fail;
508         }
509
510         ev = samba_tevent_context_init(frame);
511         if (ev == NULL) {
512                 goto fail;
513         }
514
515         req = mdscli_search_send(frame,
516                                  ev,
517                                  mdscli_ctx,
518                                  mds_query,
519                                  path_scope,
520                                  live);
521         if (req == NULL) {
522                 goto fail;
523         }
524         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
525                 goto fail;
526         }
527
528         status = mdscli_search_recv(req, mem_ctx, search);
529 fail:
530         TALLOC_FREE(frame);
531         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 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         struct tevent_req *req = NULL;
552         struct mdscli_get_results_state *state = NULL;
553         struct tevent_req *subreq = NULL;
554         struct mdscli_ctx *mdscli_ctx = search->mdscli_ctx;
555         NTSTATUS status;
556
557         req = tevent_req_create(req, &state, struct mdscli_get_results_state);
558         if (req == NULL) {
559                 return NULL;
560         }
561
562         *state = (struct mdscli_get_results_state) {
563                 .ev = ev,
564                 .search = search,
565         };
566
567         status = mdscli_blob_get_results(state,
568                                          search,
569                                          &state->request_blob);
570         if (tevent_req_nterror(req, status)) {
571                 return tevent_req_post(req, ev);
572         }
573
574         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                                         state->request_blob,
584                                         0,
585                                         mdscli_ctx->max_fragment_size,
586                                         1,
587                                         mdscli_ctx->max_fragment_size,
588                                         0,
589                                         0,
590                                         &state->fragment,
591                                         &state->response_fragment,
592                                         &mdscli_ctx->mdscmd_cmd.unkn9);
593         if (tevent_req_nomem(subreq, req)) {
594                 return tevent_req_post(req, ev);
595         }
596         tevent_req_set_callback(subreq, mdscli_get_results_cmd_done, req);
597         mdscli_ctx->async_pending++;
598         return req;
599 }
600
601 static void mdscli_get_results_cmd_done(struct tevent_req *subreq)
602 {
603         struct tevent_req *req = tevent_req_callback_data(
604                 subreq, struct tevent_req);
605         struct mdscli_get_results_state *state = tevent_req_data(
606                 req, struct mdscli_get_results_state);
607         struct mdscli_ctx *mdscli_ctx = state->search->mdscli_ctx;
608         size_t oldsize, newsize;
609         DALLOC_CTX *d = NULL;
610         uint64_t *uint64p = NULL;
611         bool search_in_progress = false;
612         sl_cnids_t *cnids = NULL;
613         size_t ncnids;
614         size_t i;
615         NTSTATUS status;
616         bool ok;
617
618         status = dcerpc_mdssvc_cmd_recv(subreq, state);
619         TALLOC_FREE(subreq);
620         state->search->mdscli_ctx->async_pending--;
621         if (tevent_req_nterror(req, status)) {
622                 return;
623         }
624
625         oldsize = state->response_data.length;
626         newsize = oldsize + state->response_fragment.length;
627         if (newsize < oldsize) {
628                 tevent_req_nterror(req, NT_STATUS_INTEGER_OVERFLOW);
629                 return;
630         }
631
632         ok = data_blob_realloc(state, &state->response_data, newsize);
633         if (!ok) {
634                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
635                 return;
636         }
637         (void)memcpy(state->response_data.data + oldsize,
638                      state->response_fragment.spotlight_blob,
639                      state->response_fragment.length);
640
641         TALLOC_FREE(state->response_fragment.spotlight_blob);
642         state->response_fragment.length = 0;
643         state->response_fragment.size = 0;
644
645         if (state->fragment != 0) {
646                 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                                 mdscli_ctx->max_fragment_size,
659                                 1,
660                                 mdscli_ctx->max_fragment_size,
661                                 0,
662                                 0,
663                                 &state->fragment,
664                                 &state->response_fragment,
665                                 &mdscli_ctx->mdscmd_cmd.unkn9);
666                 if (tevent_req_nomem(subreq, req)) {
667                         tevent_req_post(req, state->ev);
668                         return;
669                 }
670                 tevent_req_set_callback(subreq,
671                                         mdscli_get_results_cmd_done,
672                                         req);
673                 mdscli_ctx->async_pending++;
674                 return;
675         }
676
677         d = dalloc_new(state);
678         if (tevent_req_nomem(d, req)) {
679                 return;
680         }
681
682         ok = sl_unpack(d,
683                        (char *)state->response_data.data,
684                        state->response_data.length);
685         if (!ok) {
686                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
687                 return;
688         }
689
690         uint64p = dalloc_get(d,
691                              "DALLOC_CTX", 0,
692                              "uint64_t", 0);
693         if (uint64p == NULL) {
694                 DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
695                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
696                 return;
697         }
698
699         if (*uint64p == 35) {
700                 DBG_DEBUG("Search in progress\n");
701                 search_in_progress = true;
702         }
703
704         cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1);
705         if (cnids == NULL) {
706                 DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
707                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
708                 return;
709         }
710
711         ncnids = dalloc_size(cnids->ca_cnids);
712         if (ncnids == 0 && !search_in_progress) {
713                 tevent_req_nterror(req, NT_STATUS_NO_MORE_MATCHES);
714                 return;
715         }
716
717         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                 DBG_DEBUG("unexpected ca_unkn1: %s", dalloc_dump(d, 0));
723                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
724                 return;
725         }
726
727         if (cnids->ca_context != state->search->ctx_id.connection ) {
728                 DBG_DEBUG("unexpected ca_context: %s", dalloc_dump(d, 0));
729                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
730                 return;
731         }
732
733         state->cnids = talloc_zero_array(state, uint64_t, ncnids);
734         if (tevent_req_nomem(state->cnids, req)) {
735                 return;
736         }
737
738         for (i = 0; i < ncnids; i++) {
739                 uint64_t *cnid = NULL;
740
741                 cnid = dalloc_get(cnids->ca_cnids, "uint64_t", i);
742                 if (cnid == NULL) {
743                         DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
744                         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
745                         return;
746                 }
747                 state->cnids[i] = *cnid;
748         }
749
750         tevent_req_done(req);
751         return;
752 }
753
754 NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
755                                  TALLOC_CTX *mem_ctx,
756                                  uint64_t **cnids)
757 {
758         struct mdscli_get_results_state *state = tevent_req_data(
759                 req, struct mdscli_get_results_state);
760         NTSTATUS status;
761
762         if (tevent_req_is_nterror(req, &status)) {
763                 tevent_req_received(req);
764                 return status;
765         }
766
767         *cnids = talloc_move(mem_ctx, &state->cnids);
768
769         tevent_req_received(req);
770         return NT_STATUS_OK;
771 }
772
773 NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
774                             struct mdscli_search_ctx *search,
775                             uint64_t **_cnids)
776 {
777         TALLOC_CTX *frame = talloc_stackframe();
778         struct tevent_req *req = NULL;
779         struct tevent_context *ev = NULL;
780         NTSTATUS status = NT_STATUS_NO_MEMORY;
781
782         if (search->mdscli_ctx->async_pending != 0) {
783                 status = NT_STATUS_INVALID_PARAMETER;
784                 goto fail;
785         }
786
787         ev = samba_tevent_context_init(frame);
788         if (ev == NULL) {
789                 goto fail;
790         }
791
792         req = mdscli_get_results_send(frame,
793                                       ev,
794                                       search);
795         if (req == NULL) {
796                 goto fail;
797         }
798         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
799                 goto fail;
800         }
801
802         status = mdscli_get_results_recv(req, mem_ctx, _cnids);
803 fail:
804         TALLOC_FREE(frame);
805         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 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         struct tevent_req *req = NULL;
823         struct mdscli_get_path_state *state = NULL;
824         struct tevent_req *subreq = NULL;
825         NTSTATUS status;
826
827         req = tevent_req_create(req, &state, struct mdscli_get_path_state);
828         if (req == NULL) {
829                 return NULL;
830         }
831         *state = (struct mdscli_get_path_state) {
832                 .mdscli_ctx = mdscli_ctx,
833         };
834
835         status = mdscli_blob_get_path(state,
836                                       mdscli_ctx,
837                                       cnid,
838                                       &state->request_blob);
839         if (tevent_req_nterror(req, status)) {
840                 return tevent_req_post(req, ev);
841         }
842
843         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                                         state->request_blob,
853                                         0,
854                                         mdscli_ctx->max_fragment_size,
855                                         1,
856                                         mdscli_ctx->max_fragment_size,
857                                         0,
858                                         0,
859                                         &mdscli_ctx->mdscmd_cmd.fragment,
860                                         &state->response_blob,
861                                         &mdscli_ctx->mdscmd_cmd.unkn9);
862         if (tevent_req_nomem(subreq, req)) {
863                 return tevent_req_post(req, ev);
864         }
865         tevent_req_set_callback(subreq, mdscli_get_path_done, req);
866         mdscli_ctx->async_pending++;
867         return req;
868 }
869
870 static void mdscli_get_path_done(struct tevent_req *subreq)
871 {
872         struct tevent_req *req = tevent_req_callback_data(
873                 subreq, struct tevent_req);
874         struct mdscli_get_path_state *state = tevent_req_data(
875                 req, struct mdscli_get_path_state);
876         DALLOC_CTX *d = NULL;
877         size_t pathlen;
878         size_t prefixlen;
879         char *path = NULL;
880         const char *p = NULL;
881         NTSTATUS status;
882         bool ok;
883
884         status = dcerpc_mdssvc_cmd_recv(subreq, state);
885         TALLOC_FREE(subreq);
886         state->mdscli_ctx->async_pending--;
887         if (tevent_req_nterror(req, status)) {
888                 return;
889         }
890
891         d = dalloc_new(state);
892         if (tevent_req_nomem(d, req)) {
893                 return;
894         }
895
896         ok = sl_unpack(d,
897                        (char *)state->response_blob.spotlight_blob,
898                        state->response_blob.length);
899         if (!ok) {
900                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
901                 return;
902         }
903
904         path = dalloc_get(d,
905                           "DALLOC_CTX", 0,
906                           "DALLOC_CTX", 2,
907                           "DALLOC_CTX", 0,
908                           "DALLOC_CTX", 1,
909                           "char *", 0);
910         if (path == NULL) {
911                 DBG_DEBUG("No path in mds response: %s", dalloc_dump(d, 0));
912                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
913                 return;
914         }
915
916         /* Path is prefixed by /PATHSCOPE/SHARENAME/, strip it */
917         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         prefixlen = state->mdscli_ctx->path_scope_len
924                 + state->mdscli_ctx->mdscmd_open.share_path_len;
925
926         if (pathlen < prefixlen) {
927                 DBG_DEBUG("Bad path: %s\n", path);
928                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
929                 return;
930         }
931
932         p = path + prefixlen;
933         while (*p == '/') {
934                 p++;
935         }
936         if (*p == '\0') {
937                 DBG_DEBUG("Bad path: %s\n", path);
938                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
939                 return;
940         }
941
942         state->path = talloc_strdup(state, p);
943         if (state->path == NULL) {
944                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
945                 return;
946         }
947         DBG_DEBUG("path: %s\n", state->path);
948
949         tevent_req_done(req);
950         return;
951 }
952
953 NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
954                               TALLOC_CTX *mem_ctx,
955                               char **path)
956 {
957         struct mdscli_get_path_state *state = tevent_req_data(
958                 req, struct mdscli_get_path_state);
959         NTSTATUS status;
960
961         if (tevent_req_is_nterror(req, &status)) {
962                 tevent_req_received(req);
963                 return status;
964         }
965
966         *path = talloc_move(mem_ctx, &state->path);
967         tevent_req_received(req);
968         return NT_STATUS_OK;
969 }
970
971 NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
972                          struct mdscli_ctx *mdscli_ctx,
973                          uint64_t cnid,
974                          char **path)
975 {
976         TALLOC_CTX *frame = talloc_stackframe();
977         struct tevent_req *req = NULL;
978         struct tevent_context *ev = NULL;
979         NTSTATUS status = NT_STATUS_NO_MEMORY;
980
981         if (mdscli_ctx->async_pending != 0) {
982                 status = NT_STATUS_INVALID_PARAMETER;
983                 goto fail;
984         }
985
986         ev = samba_tevent_context_init(frame);
987         if (ev == NULL) {
988                 goto fail;
989         }
990
991         req = mdscli_get_path_send(frame, ev, mdscli_ctx, cnid);
992         if (req == NULL) {
993                 goto fail;
994         }
995         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
996                 goto fail;
997         }
998
999         status = mdscli_get_path_recv(req, mem_ctx, path);
1000 fail:
1001         TALLOC_FREE(frame);
1002         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 struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
1014                                             struct tevent_context *ev,
1015                                             struct mdscli_search_ctx **search)
1016 {
1017         struct mdscli_ctx *mdscli_ctx = NULL;
1018         struct tevent_req *req = NULL;
1019         struct mdscli_close_search_state *state = NULL;
1020         struct tevent_req *subreq = NULL;
1021         NTSTATUS status;
1022
1023         req = tevent_req_create(req, &state, struct mdscli_close_search_state);
1024         if (req == NULL) {
1025                 return NULL;
1026         }
1027         *state = (struct mdscli_close_search_state) {
1028                 .search = talloc_move(state, search),
1029         };
1030         mdscli_ctx = state->search->mdscli_ctx;
1031
1032         status = mdscli_blob_close_search(state,
1033                                           state->search,
1034                                           &state->request_blob);
1035         if (tevent_req_nterror(req, status)) {
1036                 return tevent_req_post(req, ev);
1037         }
1038
1039         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                                         state->request_blob,
1049                                         0,
1050                                         mdscli_ctx->max_fragment_size,
1051                                         1,
1052                                         mdscli_ctx->max_fragment_size,
1053                                         0,
1054                                         0,
1055                                         &mdscli_ctx->mdscmd_cmd.fragment,
1056                                         &state->response_blob,
1057                                         &mdscli_ctx->mdscmd_cmd.unkn9);
1058         if (tevent_req_nomem(subreq, req)) {
1059                 return tevent_req_post(req, ev);
1060         }
1061         tevent_req_set_callback(subreq, mdscli_close_search_done, req);
1062         mdscli_ctx->async_pending++;
1063         return req;
1064 }
1065
1066 static void mdscli_close_search_done(struct tevent_req *subreq)
1067 {
1068         struct tevent_req *req = tevent_req_callback_data(
1069                 subreq, struct tevent_req);
1070         struct mdscli_close_search_state *state = tevent_req_data(
1071                 req, struct mdscli_close_search_state);
1072         NTSTATUS status;
1073
1074         status = dcerpc_mdssvc_cmd_recv(subreq, state);
1075         TALLOC_FREE(subreq);
1076         state->search->mdscli_ctx->async_pending--;
1077         if (tevent_req_nterror(req, status)) {
1078                 return;
1079         }
1080
1081         tevent_req_done(req);
1082         return;
1083 }
1084
1085 NTSTATUS mdscli_close_search_recv(struct tevent_req *req)
1086 {
1087         return tevent_req_simple_recv_ntstatus(req);
1088 }
1089
1090 NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search)
1091 {
1092         TALLOC_CTX *frame = talloc_stackframe();
1093         struct tevent_req *req = NULL;
1094         struct tevent_context *ev = NULL;
1095         NTSTATUS status = NT_STATUS_NO_MEMORY;
1096
1097         if ((*search)->mdscli_ctx->async_pending != 0) {
1098                 status = NT_STATUS_INVALID_PARAMETER;
1099                 goto fail;
1100         }
1101
1102         ev = samba_tevent_context_init(frame);
1103         if (ev == NULL) {
1104                 goto fail;
1105         }
1106
1107         req = mdscli_close_search_send(frame, ev, search);
1108         if (req == NULL) {
1109                 goto fail;
1110         }
1111         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1112                 goto fail;
1113         }
1114
1115         status = mdscli_close_search_recv(req);
1116 fail:
1117         TALLOC_FREE(frame);
1118         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 struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
1128                                           struct tevent_context *ev,
1129                                           struct mdscli_ctx *mdscli_ctx)
1130 {
1131         struct tevent_req *req = NULL;
1132         struct mdscli_disconnect_state *state = NULL;
1133         struct tevent_req *subreq = NULL;
1134
1135         req = tevent_req_create(req, &state, struct mdscli_disconnect_state);
1136         if (req == NULL) {
1137                 return NULL;
1138         }
1139         *state = (struct mdscli_disconnect_state) {
1140                 .mdscli_ctx = mdscli_ctx,
1141         };
1142
1143         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         if (tevent_req_nomem(subreq, req)) {
1154                 return tevent_req_post(req, ev);
1155         }
1156         tevent_req_set_callback(subreq, mdscli_disconnect_done, req);
1157         mdscli_ctx->async_pending++;
1158         return req;
1159 }
1160
1161 static void mdscli_disconnect_done(struct tevent_req *subreq)
1162 {
1163         struct tevent_req *req = tevent_req_callback_data(
1164                 subreq, struct tevent_req);
1165         struct mdscli_disconnect_state *state = tevent_req_data(
1166                 req, struct mdscli_disconnect_state);
1167         NTSTATUS status;
1168
1169         status = dcerpc_mdssvc_close_recv(subreq, state);
1170         TALLOC_FREE(subreq);
1171         state->mdscli_ctx->async_pending--;
1172         if (tevent_req_nterror(req, status)) {
1173                 return;
1174         }
1175
1176         tevent_req_done(req);
1177         return;
1178 }
1179
1180 NTSTATUS mdscli_disconnect_recv(struct tevent_req *req)
1181 {
1182         return tevent_req_simple_recv_ntstatus(req);
1183 }
1184
1185 NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx)
1186 {
1187         TALLOC_CTX *frame = talloc_stackframe();
1188         struct tevent_req *req = NULL;
1189         struct tevent_context *ev = NULL;
1190         NTSTATUS status = NT_STATUS_NO_MEMORY;
1191
1192         if (mdscli_ctx->async_pending != 0) {
1193                 status = NT_STATUS_INVALID_PARAMETER;
1194                 goto fail;
1195         }
1196
1197         ev = samba_tevent_context_init(frame);
1198         if (ev == NULL) {
1199                 goto fail;
1200         }
1201
1202         req = mdscli_disconnect_send(frame, ev, mdscli_ctx);
1203         if (req == NULL) {
1204                 goto fail;
1205         }
1206         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1207                 goto fail;
1208         }
1209
1210         status = mdscli_disconnect_recv(req);
1211 fail:
1212         TALLOC_FREE(frame);
1213         return status;
1214 }