CVE-2022-3437 third_party/heimdal: Don't pass NULL pointers to memcpy() in DES unwrap
[Samba.git] / source3 / libsmb / clilist.c
blobda31ed2f17fc2daddba4edf680a7dbeb68c35b42
1 /*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
24 #include "trans2.h"
25 #include "../libcli/smb/smbXcli_base.h"
27 /****************************************************************************
28 Check if a returned directory name is safe.
29 ****************************************************************************/
31 static NTSTATUS is_bad_name(bool windows_names, const char *name)
33 const char *bad_name_p = NULL;
35 bad_name_p = strchr(name, '/');
36 if (bad_name_p != NULL) {
38 * Windows and POSIX names can't have '/'.
39 * Server is attacking us.
41 return NT_STATUS_INVALID_NETWORK_RESPONSE;
43 if (windows_names) {
44 bad_name_p = strchr(name, '\\');
45 if (bad_name_p != NULL) {
47 * Windows names can't have '\\'.
48 * Server is attacking us.
50 return NT_STATUS_INVALID_NETWORK_RESPONSE;
53 return NT_STATUS_OK;
56 /****************************************************************************
57 Check if a returned directory name is safe. Disconnect if server is
58 sending bad names.
59 ****************************************************************************/
61 NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
62 const struct file_info *finfo)
64 NTSTATUS status = NT_STATUS_OK;
65 bool windows_names = true;
67 if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
68 windows_names = false;
70 if (finfo->name != NULL) {
71 status = is_bad_name(windows_names, finfo->name);
72 if (!NT_STATUS_IS_OK(status)) {
73 DBG_ERR("bad finfo->name\n");
74 return status;
77 if (finfo->short_name != NULL) {
78 status = is_bad_name(windows_names, finfo->short_name);
79 if (!NT_STATUS_IS_OK(status)) {
80 DBG_ERR("bad finfo->short_name\n");
81 return status;
84 return NT_STATUS_OK;
87 /****************************************************************************
88 Calculate a safe next_entry_offset.
89 ****************************************************************************/
91 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
93 size_t next_entry_offset = (size_t)IVAL(base,0);
95 if (next_entry_offset == 0 ||
96 base + next_entry_offset < base ||
97 base + next_entry_offset > pdata_end) {
98 next_entry_offset = pdata_end - base;
100 return next_entry_offset;
103 /****************************************************************************
104 Interpret a long filename structure - this is mostly guesses at the moment.
105 The length of the structure is returned
106 The structure of a long filename depends on the info level.
107 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
108 by NT and SMB_FIND_EA_SIZE is used by OS/2
109 ****************************************************************************/
111 static size_t interpret_long_filename(TALLOC_CTX *ctx,
112 struct cli_state *cli,
113 int level,
114 const char *base_ptr,
115 uint16_t recv_flags2,
116 const char *p,
117 const char *pdata_end,
118 struct file_info *finfo,
119 uint32_t *p_resume_key,
120 DATA_BLOB *p_last_name_raw)
122 int len;
123 size_t ret;
124 const char *base = p;
126 data_blob_free(p_last_name_raw);
128 if (p_resume_key) {
129 *p_resume_key = 0;
131 ZERO_STRUCTP(finfo);
133 switch (level) {
134 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
135 /* these dates are converted to GMT by
136 make_unix_date */
137 if (pdata_end - base < 27) {
138 return pdata_end - base;
141 * What we're returning here as ctime_ts is
142 * actually the server create time.
144 finfo->btime_ts = convert_time_t_to_timespec(
145 make_unix_date2(p+4,
146 smb1cli_conn_server_time_zone(
147 cli->conn)));
148 finfo->ctime_ts = convert_time_t_to_timespec(
149 make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
150 finfo->atime_ts = convert_time_t_to_timespec(
151 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
152 finfo->mtime_ts = convert_time_t_to_timespec(
153 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
154 finfo->size = IVAL(p,16);
155 finfo->attr = SVAL(p,24);
156 len = CVAL(p, 26);
157 p += 27;
158 if (recv_flags2 & FLAGS2_UNICODE_STRINGS) {
159 p += ucs2_align(base_ptr, p, STR_UNICODE);
162 /* We can safely use len here (which is required by OS/2)
163 * and the NAS-BASIC server instead of +2 or +1 as the
164 * STR_TERMINATE flag below is
165 * actually used as the length calculation.
166 * The len is merely an upper bound.
167 * Due to the explicit 2 byte null termination
168 * in cli_receive_trans/cli_receive_nt_trans
169 * we know this is safe. JRA + kukks
172 if (p + len > pdata_end) {
173 return pdata_end - base;
176 /* the len+2 below looks strange but it is
177 important to cope with the differences
178 between win2000 and win9x for this call
179 (tridge) */
180 ret = pull_string_talloc(ctx,
181 base_ptr,
182 recv_flags2,
183 &finfo->name,
185 len+2,
186 STR_TERMINATE);
187 if (ret == (size_t)-1) {
188 return pdata_end - base;
190 p += ret;
191 return PTR_DIFF(p, base);
193 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
194 /* these dates are converted to GMT by
195 make_unix_date */
196 if (pdata_end - base < 31) {
197 return pdata_end - base;
200 * What we're returning here as ctime_ts is
201 * actually the server create time.
203 finfo->btime_ts = convert_time_t_to_timespec(
204 make_unix_date2(p+4,
205 smb1cli_conn_server_time_zone(
206 cli->conn)));
207 finfo->ctime_ts = convert_time_t_to_timespec(
208 make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
209 finfo->atime_ts = convert_time_t_to_timespec(
210 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
211 finfo->mtime_ts = convert_time_t_to_timespec(
212 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
213 finfo->size = IVAL(p,16);
214 finfo->attr = SVAL(p,24);
215 len = CVAL(p, 30);
216 p += 31;
217 /* check for unisys! */
218 if (p + len + 1 > pdata_end) {
219 return pdata_end - base;
221 ret = pull_string_talloc(ctx,
222 base_ptr,
223 recv_flags2,
224 &finfo->name,
226 len,
227 STR_NOALIGN);
228 if (ret == (size_t)-1) {
229 return pdata_end - base;
231 p += ret;
232 return PTR_DIFF(p, base) + 1;
234 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
236 size_t namelen, slen;
238 if (pdata_end - base < 94) {
239 return pdata_end - base;
242 p += 4; /* next entry offset */
244 if (p_resume_key) {
245 *p_resume_key = IVAL(p,0);
247 p += 4; /* fileindex */
249 /* Offset zero is "create time", not "change time". */
250 p += 8;
251 finfo->atime_ts = interpret_long_date(p);
252 p += 8;
253 finfo->mtime_ts = interpret_long_date(p);
254 p += 8;
255 finfo->ctime_ts = interpret_long_date(p);
256 p += 8;
257 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
258 p += 8;
259 p += 8; /* alloc size */
260 finfo->attr = IVAL(p,0);
261 p += 4;
262 namelen = IVAL(p,0);
263 p += 4;
264 p += 4; /* EA size */
265 slen = CVAL(p, 0);
266 if (slen > 24) {
267 /* Bad short name length. */
268 return pdata_end - base;
270 p += 2;
271 ret = pull_string_talloc(ctx,
272 base_ptr,
273 recv_flags2,
274 &finfo->short_name,
276 slen,
277 STR_UNICODE);
278 if (ret == (size_t)-1) {
279 return pdata_end - base;
281 p += 24; /* short name? */
282 if (p + namelen < p || p + namelen > pdata_end) {
283 return pdata_end - base;
285 ret = pull_string_talloc(ctx,
286 base_ptr,
287 recv_flags2,
288 &finfo->name,
290 namelen,
292 if (ret == (size_t)-1) {
293 return pdata_end - base;
296 /* To be robust in the face of unicode conversion failures
297 we need to copy the raw bytes of the last name seen here.
298 Namelen doesn't include the terminating unicode null, so
299 copy it here. */
301 if (p_last_name_raw) {
302 *p_last_name_raw = data_blob(NULL, namelen+2);
303 memcpy(p_last_name_raw->data, p, namelen);
304 SSVAL(p_last_name_raw->data, namelen, 0);
306 return calc_next_entry_offset(base, pdata_end);
310 DEBUG(1,("Unknown long filename format %d\n",level));
311 return calc_next_entry_offset(base, pdata_end);
314 /****************************************************************************
315 Interpret a short filename structure.
316 The length of the structure is returned.
317 ****************************************************************************/
319 static bool interpret_short_filename(TALLOC_CTX *ctx,
320 struct cli_state *cli,
321 char *p,
322 struct file_info *finfo)
324 size_t ret;
325 ZERO_STRUCTP(finfo);
327 finfo->attr = CVAL(p,21);
329 /* We don't get birth time. */
330 finfo->btime_ts.tv_sec = 0;
331 finfo->btime_ts.tv_nsec = 0;
332 /* this date is converted to GMT by make_unix_date */
333 finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
334 finfo->ctime_ts.tv_nsec = 0;
335 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
336 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
337 finfo->size = IVAL(p,26);
338 ret = pull_string_talloc(ctx,
339 NULL,
341 &finfo->name,
342 p+30,
344 STR_ASCII);
345 if (ret == (size_t)-1) {
346 return false;
349 if (finfo->name) {
350 finfo->short_name = talloc_strdup(ctx, finfo->name);
351 if (finfo->short_name == NULL) {
352 return false;
355 return true;
358 struct cli_list_old_state {
359 struct tevent_context *ev;
360 struct cli_state *cli;
361 uint16_t vwv[2];
362 char *mask;
363 int num_asked;
364 uint32_t attribute;
365 uint8_t search_status[23];
366 bool first;
367 bool done;
368 uint8_t *dirlist;
371 static void cli_list_old_done(struct tevent_req *subreq);
373 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
374 struct tevent_context *ev,
375 struct cli_state *cli,
376 const char *mask,
377 uint32_t attribute)
379 struct tevent_req *req, *subreq;
380 struct cli_list_old_state *state;
381 uint8_t *bytes;
382 static const uint16_t zero = 0;
383 uint32_t usable_space;
385 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
386 if (req == NULL) {
387 return NULL;
389 state->ev = ev;
390 state->cli = cli;
391 state->attribute = attribute;
392 state->first = true;
393 state->mask = talloc_strdup(state, mask);
394 if (tevent_req_nomem(state->mask, req)) {
395 return tevent_req_post(req, ev);
397 state->mask = smb1_dfs_share_path(state, cli, state->mask);
398 if (tevent_req_nomem(state->mask, req)) {
399 return tevent_req_post(req, ev);
401 usable_space = cli_state_available_size(cli, 100);
402 state->num_asked = usable_space / DIR_STRUCT_SIZE;
404 SSVAL(state->vwv + 0, 0, state->num_asked);
405 SSVAL(state->vwv + 1, 0, state->attribute);
407 bytes = talloc_array(state, uint8_t, 1);
408 if (tevent_req_nomem(bytes, req)) {
409 return tevent_req_post(req, ev);
411 bytes[0] = 4;
412 bytes = smb_bytes_push_str(bytes,
413 smbXcli_conn_use_unicode(cli->conn),
414 state->mask,
415 strlen(state->mask)+1,
416 NULL);
418 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
419 if (tevent_req_nomem(bytes, req)) {
420 return tevent_req_post(req, ev);
423 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
424 2, state->vwv, talloc_get_size(bytes), bytes);
425 if (tevent_req_nomem(subreq, req)) {
426 return tevent_req_post(req, ev);
428 tevent_req_set_callback(subreq, cli_list_old_done, req);
429 return req;
432 static void cli_list_old_done(struct tevent_req *subreq)
434 struct tevent_req *req = tevent_req_callback_data(
435 subreq, struct tevent_req);
436 struct cli_list_old_state *state = tevent_req_data(
437 req, struct cli_list_old_state);
438 NTSTATUS status;
439 uint8_t cmd;
440 uint8_t wct;
441 uint16_t *vwv;
442 uint32_t num_bytes;
443 uint8_t *bytes;
444 uint16_t received;
445 size_t dirlist_len;
446 uint8_t *tmp;
448 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
449 &bytes);
450 if (!NT_STATUS_IS_OK(status)
451 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
452 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
453 TALLOC_FREE(subreq);
454 tevent_req_nterror(req, status);
455 return;
457 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
458 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
459 received = 0;
460 } else {
461 if (wct < 1) {
462 TALLOC_FREE(subreq);
463 tevent_req_nterror(
464 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
465 return;
467 received = SVAL(vwv + 0, 0);
470 if (received > 0) {
472 * I don't think this can wrap. received is
473 * initialized from a 16-bit value.
475 if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
476 TALLOC_FREE(subreq);
477 tevent_req_nterror(
478 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
479 return;
482 dirlist_len = talloc_get_size(state->dirlist);
484 tmp = talloc_realloc(
485 state, state->dirlist, uint8_t,
486 dirlist_len + received * DIR_STRUCT_SIZE);
487 if (tevent_req_nomem(tmp, req)) {
488 return;
490 state->dirlist = tmp;
491 memcpy(state->dirlist + dirlist_len, bytes + 3,
492 received * DIR_STRUCT_SIZE);
494 SSVAL(state->search_status, 0, 21);
495 memcpy(state->search_status + 2,
496 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
497 cmd = SMBsearch;
498 } else {
499 if (state->first || state->done) {
500 tevent_req_done(req);
501 return;
503 state->done = true;
504 state->num_asked = 0;
505 cmd = SMBfclose;
507 TALLOC_FREE(subreq);
509 state->first = false;
511 SSVAL(state->vwv + 0, 0, state->num_asked);
512 SSVAL(state->vwv + 1, 0, state->attribute);
514 bytes = talloc_array(state, uint8_t, 1);
515 if (tevent_req_nomem(bytes, req)) {
516 return;
518 bytes[0] = 4;
519 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
520 1, NULL);
521 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
522 sizeof(state->search_status));
523 if (tevent_req_nomem(bytes, req)) {
524 return;
526 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
527 2, state->vwv, talloc_get_size(bytes), bytes);
528 if (tevent_req_nomem(subreq, req)) {
529 return;
531 tevent_req_set_callback(subreq, cli_list_old_done, req);
534 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
535 struct file_info **pfinfo)
537 struct cli_list_old_state *state = tevent_req_data(
538 req, struct cli_list_old_state);
539 NTSTATUS status;
540 size_t i, num_received;
541 struct file_info *finfo;
543 if (tevent_req_is_nterror(req, &status)) {
544 return status;
547 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
549 finfo = talloc_array(mem_ctx, struct file_info, num_received);
550 if (finfo == NULL) {
551 return NT_STATUS_NO_MEMORY;
554 for (i=0; i<num_received; i++) {
555 if (!interpret_short_filename(
556 finfo, state->cli,
557 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
558 &finfo[i])) {
559 TALLOC_FREE(finfo);
560 return NT_STATUS_NO_MEMORY;
562 if (finfo->name == NULL) {
563 TALLOC_FREE(finfo);
564 return NT_STATUS_INVALID_NETWORK_RESPONSE;
566 status = is_bad_finfo_name(state->cli, finfo);
567 if (!NT_STATUS_IS_OK(status)) {
568 smbXcli_conn_disconnect(state->cli->conn, status);
569 TALLOC_FREE(finfo);
570 return status;
573 *pfinfo = finfo;
574 return NT_STATUS_OK;
577 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
578 uint32_t attribute,
579 NTSTATUS (*fn)(struct file_info *,
580 const char *, void *), void *state)
582 TALLOC_CTX *frame = talloc_stackframe();
583 struct tevent_context *ev;
584 struct tevent_req *req;
585 NTSTATUS status = NT_STATUS_NO_MEMORY;
586 struct file_info *finfo = NULL;
587 size_t i, num_finfo;
589 if (smbXcli_conn_has_async_calls(cli->conn)) {
591 * Can't use sync call while an async call is in flight
593 status = NT_STATUS_INVALID_PARAMETER;
594 goto fail;
596 ev = samba_tevent_context_init(frame);
597 if (ev == NULL) {
598 goto fail;
600 req = cli_list_old_send(frame, ev, cli, mask, attribute);
601 if (req == NULL) {
602 goto fail;
604 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
605 goto fail;
607 status = cli_list_old_recv(req, frame, &finfo);
608 if (!NT_STATUS_IS_OK(status)) {
609 goto fail;
611 num_finfo = talloc_array_length(finfo);
612 for (i=0; i<num_finfo; i++) {
613 status = fn(&finfo[i], mask, state);
614 if (!NT_STATUS_IS_OK(status)) {
615 goto fail;
618 fail:
619 TALLOC_FREE(frame);
620 return status;
623 struct cli_list_trans_state {
624 struct tevent_context *ev;
625 struct cli_state *cli;
626 char *mask;
627 uint32_t attribute;
628 uint16_t info_level;
630 int loop_count;
631 int total_received;
632 uint16_t max_matches;
633 bool first;
635 int ff_eos;
636 int ff_dir_handle;
638 uint16_t setup[1];
639 uint8_t *param;
641 struct file_info *finfo;
644 static void cli_list_trans_done(struct tevent_req *subreq);
646 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
647 struct tevent_context *ev,
648 struct cli_state *cli,
649 const char *mask,
650 uint32_t attribute,
651 uint16_t info_level)
653 struct tevent_req *req, *subreq;
654 struct cli_list_trans_state *state;
655 size_t param_len;
656 uint16_t additional_flags2 = 0;
658 req = tevent_req_create(mem_ctx, &state,
659 struct cli_list_trans_state);
660 if (req == NULL) {
661 return NULL;
663 state->ev = ev;
664 state->cli = cli;
665 state->mask = talloc_strdup(state, mask);
666 if (tevent_req_nomem(state->mask, req)) {
667 return tevent_req_post(req, ev);
669 state->mask = smb1_dfs_share_path(state, cli, state->mask);
670 if (tevent_req_nomem(state->mask, req)) {
671 return tevent_req_post(req, ev);
673 state->attribute = attribute;
674 state->info_level = info_level;
675 state->loop_count = 0;
676 state->first = true;
678 state->max_matches = 1366; /* Match W2k */
680 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
682 state->param = talloc_array(state, uint8_t, 12);
683 if (tevent_req_nomem(state->param, req)) {
684 return tevent_req_post(req, ev);
687 SSVAL(state->param, 0, state->attribute);
688 SSVAL(state->param, 2, state->max_matches);
689 SSVAL(state->param, 4,
690 FLAG_TRANS2_FIND_REQUIRE_RESUME
691 |FLAG_TRANS2_FIND_CLOSE_IF_END
692 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
693 SSVAL(state->param, 6, state->info_level);
694 SIVAL(state->param, 8, 0);
696 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
697 state->mask, strlen(state->mask)+1,
698 NULL);
699 if (tevent_req_nomem(state->param, req)) {
700 return tevent_req_post(req, ev);
703 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
704 additional_flags2 = FLAGS2_REPARSE_PATH;
707 param_len = talloc_get_size(state->param);
709 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
710 SMBtrans2, NULL, -1, 0, 0,
711 state->setup, 1, 0,
712 state->param, param_len, 10,
713 NULL, 0, CLI_BUFFER_SIZE);
714 if (tevent_req_nomem(subreq, req)) {
715 return tevent_req_post(req, ev);
717 tevent_req_set_callback(subreq, cli_list_trans_done, req);
718 return req;
721 static void cli_list_trans_done(struct tevent_req *subreq)
723 struct tevent_req *req = tevent_req_callback_data(
724 subreq, struct tevent_req);
725 struct cli_list_trans_state *state = tevent_req_data(
726 req, struct cli_list_trans_state);
727 NTSTATUS status;
728 uint8_t *param;
729 uint32_t num_param;
730 uint8_t *data;
731 char *data_end;
732 uint32_t num_data;
733 uint32_t min_param;
734 struct file_info *tmp;
735 size_t old_num_finfo;
736 uint16_t recv_flags2;
737 int ff_searchcount;
738 bool ff_eos;
739 char *p, *p2;
740 uint32_t resume_key = 0;
741 int i;
742 DATA_BLOB last_name_raw;
743 struct file_info *finfo = NULL;
744 size_t param_len;
745 uint16_t additional_flags2 = 0;
747 min_param = (state->first ? 6 : 4);
749 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
750 NULL, 0, NULL,
751 &param, min_param, &num_param,
752 &data, 0, &num_data);
753 TALLOC_FREE(subreq);
754 if (!NT_STATUS_IS_OK(status)) {
756 * TODO: retry, OS/2 nofiles
758 tevent_req_nterror(req, status);
759 return;
762 if (state->first) {
763 state->ff_dir_handle = SVAL(param, 0);
764 ff_searchcount = SVAL(param, 2);
765 ff_eos = SVAL(param, 4) != 0;
766 } else {
767 ff_searchcount = SVAL(param, 0);
768 ff_eos = SVAL(param, 2) != 0;
771 old_num_finfo = talloc_array_length(state->finfo);
773 tmp = talloc_realloc(state, state->finfo, struct file_info,
774 old_num_finfo + ff_searchcount);
775 if (tevent_req_nomem(tmp, req)) {
776 return;
778 state->finfo = tmp;
780 p2 = p = (char *)data;
781 data_end = (char *)data + num_data;
782 last_name_raw = data_blob_null;
784 for (i=0; i<ff_searchcount; i++) {
785 if (p2 >= data_end) {
786 ff_eos = true;
787 break;
789 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
790 && (i == ff_searchcount-1)) {
791 /* Last entry - fixup the last offset length. */
792 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
795 data_blob_free(&last_name_raw);
797 finfo = &state->finfo[old_num_finfo + i];
799 p2 += interpret_long_filename(
800 state->finfo, /* Stick fname to the array as such */
801 state->cli, state->info_level,
802 (char *)data, recv_flags2, p2,
803 data_end, finfo, &resume_key, &last_name_raw);
805 if (finfo->name == NULL) {
806 DEBUG(1, ("cli_list: Error: unable to parse name from "
807 "info level %d\n", state->info_level));
808 tevent_req_nterror(req,
809 NT_STATUS_INVALID_NETWORK_RESPONSE);
810 return;
813 status = is_bad_finfo_name(state->cli, finfo);
814 if (!NT_STATUS_IS_OK(status)) {
815 smbXcli_conn_disconnect(state->cli->conn, status);
816 tevent_req_nterror(req, status);
817 return;
820 if (!state->first && (state->mask[0] != '\0') &&
821 strcsequal(finfo->name, state->mask)) {
822 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
823 "already been seen?\n", finfo->name));
824 ff_eos = true;
825 break;
829 if (ff_searchcount == 0) {
830 ff_eos = true;
833 TALLOC_FREE(param);
834 TALLOC_FREE(data);
837 * Shrink state->finfo to the real length we received
839 tmp = talloc_realloc(state, state->finfo, struct file_info,
840 old_num_finfo + i);
841 if (tevent_req_nomem(tmp, req)) {
842 return;
844 state->finfo = tmp;
846 state->first = false;
848 if (ff_eos) {
849 data_blob_free(&last_name_raw);
850 tevent_req_done(req);
851 return;
854 TALLOC_FREE(state->mask);
855 state->mask = talloc_strdup(state, finfo->name);
856 if (tevent_req_nomem(state->mask, req)) {
857 return;
860 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
862 param = talloc_realloc(state, state->param, uint8_t, 12);
863 if (tevent_req_nomem(param, req)) {
864 return;
866 state->param = param;
868 SSVAL(param, 0, state->ff_dir_handle);
869 SSVAL(param, 2, state->max_matches); /* max count */
870 SSVAL(param, 4, state->info_level);
872 * For W2K servers serving out FAT filesystems we *must* set
873 * the resume key. If it's not FAT then it's returned as zero.
875 SIVAL(param, 6, resume_key); /* ff_resume_key */
877 * NB. *DON'T* use continue here. If you do it seems that W2K
878 * and bretheren can miss filenames. Use last filename
879 * continue instead. JRA
881 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
882 |FLAG_TRANS2_FIND_CLOSE_IF_END
883 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
884 if (last_name_raw.length) {
885 state->param = trans2_bytes_push_bytes(state->param,
886 last_name_raw.data,
887 last_name_raw.length);
888 if (tevent_req_nomem(state->param, req)) {
889 return;
891 data_blob_free(&last_name_raw);
892 } else {
893 state->param = trans2_bytes_push_str(state->param,
894 smbXcli_conn_use_unicode(state->cli->conn),
895 state->mask,
896 strlen(state->mask)+1,
897 NULL);
898 if (tevent_req_nomem(state->param, req)) {
899 return;
902 param_len = talloc_get_size(state->param);
904 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
905 additional_flags2 = FLAGS2_REPARSE_PATH;
908 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
909 SMBtrans2, NULL, -1, 0, 0,
910 state->setup, 1, 0,
911 state->param, param_len, 10,
912 NULL, 0, CLI_BUFFER_SIZE);
913 if (tevent_req_nomem(subreq, req)) {
914 return;
916 tevent_req_set_callback(subreq, cli_list_trans_done, req);
919 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
920 TALLOC_CTX *mem_ctx,
921 struct file_info **finfo)
923 struct cli_list_trans_state *state = tevent_req_data(
924 req, struct cli_list_trans_state);
925 NTSTATUS status;
927 if (tevent_req_is_nterror(req, &status)) {
928 return status;
930 *finfo = talloc_move(mem_ctx, &state->finfo);
931 return NT_STATUS_OK;
934 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
935 uint32_t attribute, int info_level,
936 NTSTATUS (*fn)(
937 struct file_info *finfo,
938 const char *mask,
939 void *private_data),
940 void *private_data)
942 TALLOC_CTX *frame = talloc_stackframe();
943 struct tevent_context *ev;
944 struct tevent_req *req;
945 int i, num_finfo;
946 struct file_info *finfo = NULL;
947 NTSTATUS status = NT_STATUS_NO_MEMORY;
949 if (smbXcli_conn_has_async_calls(cli->conn)) {
951 * Can't use sync call while an async call is in flight
953 status = NT_STATUS_INVALID_PARAMETER;
954 goto fail;
956 ev = samba_tevent_context_init(frame);
957 if (ev == NULL) {
958 goto fail;
960 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
961 if (req == NULL) {
962 goto fail;
964 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
965 goto fail;
967 status = cli_list_trans_recv(req, frame, &finfo);
968 if (!NT_STATUS_IS_OK(status)) {
969 goto fail;
971 num_finfo = talloc_array_length(finfo);
972 for (i=0; i<num_finfo; i++) {
973 status = fn(&finfo[i], mask, private_data);
974 if (!NT_STATUS_IS_OK(status)) {
975 goto fail;
978 fail:
979 TALLOC_FREE(frame);
980 return status;
983 struct cli_list_state {
984 struct tevent_context *ev;
985 struct tevent_req *subreq;
986 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
987 struct file_info **finfo);
988 struct file_info *finfo;
989 size_t num_received;
992 static void cli_list_done(struct tevent_req *subreq);
994 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
995 struct tevent_context *ev,
996 struct cli_state *cli,
997 const char *mask,
998 uint32_t attribute,
999 uint16_t info_level)
1001 struct tevent_req *req = NULL;
1002 struct cli_list_state *state;
1003 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1005 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
1006 if (req == NULL) {
1007 return NULL;
1009 state->ev = ev;
1011 if (proto >= PROTOCOL_SMB2_02) {
1012 state->subreq = cli_smb2_list_send(state, ev, cli, mask);
1013 state->recv_fn = cli_smb2_list_recv;
1014 } else if (proto >= PROTOCOL_LANMAN2) {
1015 state->subreq = cli_list_trans_send(
1016 state, ev, cli, mask, attribute, info_level);
1017 state->recv_fn = cli_list_trans_recv;
1018 } else {
1019 state->subreq = cli_list_old_send(
1020 state, ev, cli, mask, attribute);
1021 state->recv_fn = cli_list_old_recv;
1023 if (tevent_req_nomem(state->subreq, req)) {
1024 return tevent_req_post(req, ev);
1026 tevent_req_set_callback(state->subreq, cli_list_done, req);
1027 return req;
1030 static void cli_list_done(struct tevent_req *subreq)
1032 struct tevent_req *req = tevent_req_callback_data(
1033 subreq, struct tevent_req);
1034 struct cli_list_state *state = tevent_req_data(
1035 req, struct cli_list_state);
1036 NTSTATUS status;
1038 SMB_ASSERT(subreq == state->subreq);
1041 * We don't want to be called by the lowerlevel routines
1042 * from within state->recv_fn()
1044 tevent_req_set_callback(subreq, NULL, NULL);
1046 status = state->recv_fn(subreq, state, &state->finfo);
1047 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1048 /* We'll get back here */
1049 tevent_req_set_callback(subreq, cli_list_done, req);
1050 return;
1053 if (tevent_req_nterror(req, status)) {
1054 return;
1056 tevent_req_notify_callback(req);
1059 NTSTATUS cli_list_recv(
1060 struct tevent_req *req,
1061 TALLOC_CTX *mem_ctx,
1062 struct file_info **pfinfo)
1064 struct cli_list_state *state = tevent_req_data(
1065 req, struct cli_list_state);
1066 size_t num_results;
1067 struct file_info *finfo = NULL;
1068 NTSTATUS status;
1069 bool in_progress;
1071 in_progress = tevent_req_is_in_progress(req);
1073 if (!in_progress) {
1074 if (!tevent_req_is_nterror(req, &status)) {
1075 status = NT_STATUS_NO_MORE_FILES;
1077 return status;
1080 if (state->finfo == NULL) {
1081 status = state->recv_fn(state->subreq, state, &state->finfo);
1083 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1084 tevent_req_set_callback(
1085 state->subreq, cli_list_done, req);
1086 return NT_STATUS_RETRY;
1089 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1090 status = NT_STATUS_NO_MORE_FILES;
1093 if (tevent_req_nterror(req, status)) {
1094 return status;
1097 state->num_received = 0;
1100 num_results = talloc_array_length(state->finfo);
1102 if (num_results == 1) {
1103 finfo = talloc_move(mem_ctx, &state->finfo);
1104 } else {
1105 struct file_info *src_finfo =
1106 &state->finfo[state->num_received];
1108 finfo = talloc(mem_ctx, struct file_info);
1109 if (finfo == NULL) {
1110 return NT_STATUS_NO_MEMORY;
1112 *finfo = *src_finfo;
1113 finfo->name = talloc_move(finfo, &src_finfo->name);
1114 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1117 state->num_received += 1;
1119 if (state->num_received == num_results) {
1120 TALLOC_FREE(state->finfo);
1123 tevent_req_defer_callback(req, state->ev);
1124 tevent_req_notify_callback(req);
1126 *pfinfo = finfo;
1127 return NT_STATUS_OK;
1130 struct cli_list_sync_state {
1131 const char *mask;
1132 uint32_t attribute;
1133 NTSTATUS (*fn)(struct file_info *finfo,
1134 const char *mask,
1135 void *private_data);
1136 void *private_data;
1137 NTSTATUS status;
1138 bool processed_file;
1141 static void cli_list_sync_cb(struct tevent_req *subreq)
1143 struct cli_list_sync_state *state =
1144 tevent_req_callback_data_void(subreq);
1145 struct file_info *finfo;
1146 bool ok;
1148 state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1149 /* No TALLOC_FREE(subreq), we get here more than once */
1151 if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1153 * The lowlevel SMB call was rearmed, we'll get back
1154 * here when it's done.
1156 state->status = NT_STATUS_OK;
1157 return;
1160 if (!NT_STATUS_IS_OK(state->status)) {
1161 return;
1164 ok = dir_check_ftype(finfo->attr, state->attribute);
1165 if (!ok) {
1167 * Only process if attributes match. On SMB1 server
1168 * does this, so on SMB2 we need to emulate in the
1169 * client.
1171 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1173 return;
1176 state->status = state->fn(finfo, state->mask, state->private_data);
1178 state->processed_file = true;
1180 TALLOC_FREE(finfo);
1183 NTSTATUS cli_list(struct cli_state *cli,
1184 const char *mask,
1185 uint32_t attribute,
1186 NTSTATUS (*fn)(struct file_info *finfo,
1187 const char *mask,
1188 void *private_data),
1189 void *private_data)
1191 TALLOC_CTX *frame = NULL;
1192 struct cli_list_sync_state state = {
1193 .mask = mask,
1194 .attribute = attribute,
1195 .fn = fn,
1196 .private_data = private_data,
1198 struct tevent_context *ev;
1199 struct tevent_req *req;
1200 NTSTATUS status = NT_STATUS_NO_MEMORY;
1201 uint16_t info_level;
1203 frame = talloc_stackframe();
1205 if (smbXcli_conn_has_async_calls(cli->conn)) {
1207 * Can't use sync call while an async call is in flight
1209 status = NT_STATUS_INVALID_PARAMETER;
1210 goto fail;
1212 ev = samba_tevent_context_init(frame);
1213 if (ev == NULL) {
1214 goto fail;
1217 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1218 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1220 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1221 if (req == NULL) {
1222 goto fail;
1224 tevent_req_set_callback(req, cli_list_sync_cb, &state);
1226 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1227 goto fail;
1230 status = state.status;
1232 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1233 status = NT_STATUS_OK;
1236 if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1237 status = NT_STATUS_NO_SUCH_FILE;
1240 fail:
1241 TALLOC_FREE(frame);
1242 return status;