Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : client directory list routines
4 : Copyright (C) Andrew Tridgell 1994-1998
5 :
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.
10 :
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.
15 :
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/>.
18 : */
19 :
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"
26 :
27 : /****************************************************************************
28 : Check if a returned directory name is safe.
29 : ****************************************************************************/
30 :
31 91643 : static NTSTATUS is_bad_name(bool windows_names, const char *name)
32 : {
33 91643 : const char *bad_name_p = NULL;
34 :
35 91643 : bad_name_p = strchr(name, '/');
36 91643 : if (bad_name_p != NULL) {
37 : /*
38 : * Windows and POSIX names can't have '/'.
39 : * Server is attacking us.
40 : */
41 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
42 : }
43 91643 : if (windows_names) {
44 91507 : bad_name_p = strchr(name, '\\');
45 91507 : if (bad_name_p != NULL) {
46 : /*
47 : * Windows names can't have '\\'.
48 : * Server is attacking us.
49 : */
50 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
51 : }
52 : }
53 91643 : return NT_STATUS_OK;
54 : }
55 :
56 : /****************************************************************************
57 : Check if a returned directory name is safe. Disconnect if server is
58 : sending bad names.
59 : ****************************************************************************/
60 :
61 65392 : NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
62 : const struct file_info *finfo)
63 : {
64 65392 : NTSTATUS status = NT_STATUS_OK;
65 65392 : bool windows_names = true;
66 :
67 65392 : if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
68 136 : windows_names = false;
69 : }
70 65392 : if (finfo->name != NULL) {
71 65392 : status = is_bad_name(windows_names, finfo->name);
72 65392 : if (!NT_STATUS_IS_OK(status)) {
73 0 : DBG_ERR("bad finfo->name\n");
74 0 : return status;
75 : }
76 : }
77 65392 : if (finfo->short_name != NULL) {
78 26251 : status = is_bad_name(windows_names, finfo->short_name);
79 26251 : if (!NT_STATUS_IS_OK(status)) {
80 0 : DBG_ERR("bad finfo->short_name\n");
81 0 : return status;
82 : }
83 : }
84 65392 : return NT_STATUS_OK;
85 : }
86 :
87 : /****************************************************************************
88 : Calculate a safe next_entry_offset.
89 : ****************************************************************************/
90 :
91 9257 : static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
92 : {
93 9257 : size_t next_entry_offset = (size_t)IVAL(base,0);
94 :
95 9257 : if (next_entry_offset == 0 ||
96 9257 : base + next_entry_offset < base ||
97 9257 : base + next_entry_offset > pdata_end) {
98 0 : next_entry_offset = pdata_end - base;
99 : }
100 9257 : return next_entry_offset;
101 : }
102 :
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 : ****************************************************************************/
110 :
111 9257 : 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)
121 : {
122 0 : int len;
123 0 : size_t ret;
124 9257 : const char *base = p;
125 :
126 9257 : data_blob_free(p_last_name_raw);
127 :
128 9257 : if (p_resume_key) {
129 9257 : *p_resume_key = 0;
130 : }
131 9257 : ZERO_STRUCTP(finfo);
132 :
133 9257 : switch (level) {
134 0 : case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
135 : /* these dates are converted to GMT by
136 : make_unix_date */
137 0 : if (pdata_end - base < 27) {
138 0 : return pdata_end - base;
139 : }
140 : /*
141 : * What we're returning here as ctime_ts is
142 : * actually the server create time.
143 : */
144 0 : finfo->btime_ts = convert_time_t_to_timespec(
145 0 : make_unix_date2(p+4,
146 : smb1cli_conn_server_time_zone(
147 : cli->conn)));
148 0 : finfo->ctime_ts = convert_time_t_to_timespec(
149 0 : make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
150 0 : finfo->atime_ts = convert_time_t_to_timespec(
151 0 : make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
152 0 : finfo->mtime_ts = convert_time_t_to_timespec(
153 0 : make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
154 0 : finfo->size = IVAL(p,16);
155 0 : finfo->attr = SVAL(p,24);
156 0 : len = CVAL(p, 26);
157 0 : p += 27;
158 0 : if (recv_flags2 & FLAGS2_UNICODE_STRINGS) {
159 0 : p += ucs2_align(base_ptr, p, STR_UNICODE);
160 : }
161 :
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
170 : */
171 :
172 0 : if (p + len > pdata_end) {
173 0 : return pdata_end - base;
174 : }
175 :
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 0 : ret = pull_string_talloc(ctx,
181 : base_ptr,
182 : recv_flags2,
183 : &finfo->name,
184 : p,
185 0 : len+2,
186 : STR_TERMINATE);
187 0 : if (ret == (size_t)-1) {
188 0 : return pdata_end - base;
189 : }
190 0 : p += ret;
191 0 : return PTR_DIFF(p, base);
192 :
193 0 : 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 0 : if (pdata_end - base < 31) {
197 0 : return pdata_end - base;
198 : }
199 : /*
200 : * What we're returning here as ctime_ts is
201 : * actually the server create time.
202 : */
203 0 : finfo->btime_ts = convert_time_t_to_timespec(
204 0 : make_unix_date2(p+4,
205 : smb1cli_conn_server_time_zone(
206 : cli->conn)));
207 0 : finfo->ctime_ts = convert_time_t_to_timespec(
208 0 : make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
209 0 : finfo->atime_ts = convert_time_t_to_timespec(
210 0 : make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
211 0 : finfo->mtime_ts = convert_time_t_to_timespec(
212 0 : make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
213 0 : finfo->size = IVAL(p,16);
214 0 : finfo->attr = SVAL(p,24);
215 0 : len = CVAL(p, 30);
216 0 : p += 31;
217 : /* check for unisys! */
218 0 : if (p + len + 1 > pdata_end) {
219 0 : return pdata_end - base;
220 : }
221 0 : ret = pull_string_talloc(ctx,
222 : base_ptr,
223 : recv_flags2,
224 : &finfo->name,
225 : p,
226 : len,
227 : STR_NOALIGN);
228 0 : if (ret == (size_t)-1) {
229 0 : return pdata_end - base;
230 : }
231 0 : p += ret;
232 0 : return PTR_DIFF(p, base) + 1;
233 :
234 9257 : case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
235 : {
236 0 : size_t namelen, slen;
237 :
238 9257 : if (pdata_end - base < 94) {
239 0 : return pdata_end - base;
240 : }
241 :
242 9257 : p += 4; /* next entry offset */
243 :
244 9257 : if (p_resume_key) {
245 9257 : *p_resume_key = IVAL(p,0);
246 : }
247 9257 : p += 4; /* fileindex */
248 :
249 : /* Offset zero is "create time", not "change time". */
250 9257 : p += 8;
251 9257 : finfo->atime_ts = interpret_long_date(BVAL(p, 0));
252 9257 : p += 8;
253 9257 : finfo->mtime_ts = interpret_long_date(BVAL(p, 0));
254 9257 : p += 8;
255 9257 : finfo->ctime_ts = interpret_long_date(BVAL(p, 0));
256 9257 : p += 8;
257 9257 : finfo->size = BVAL(p,0);
258 9257 : p += 8;
259 9257 : p += 8; /* alloc size */
260 9257 : finfo->attr = IVAL(p,0);
261 9257 : p += 4;
262 9257 : namelen = IVAL(p,0);
263 9257 : p += 4;
264 9257 : p += 4; /* EA size */
265 9257 : slen = CVAL(p, 0);
266 9257 : if (slen > 24) {
267 : /* Bad short name length. */
268 0 : return pdata_end - base;
269 : }
270 9257 : p += 2;
271 9257 : ret = pull_string_talloc(ctx,
272 : base_ptr,
273 : recv_flags2,
274 : &finfo->short_name,
275 : p,
276 : slen,
277 : STR_UNICODE);
278 9257 : if (ret == (size_t)-1) {
279 0 : return pdata_end - base;
280 : }
281 9257 : p += 24; /* short name? */
282 9257 : if (p + namelen < p || p + namelen > pdata_end) {
283 0 : return pdata_end - base;
284 : }
285 9257 : ret = pull_string_talloc(ctx,
286 : base_ptr,
287 : recv_flags2,
288 : &finfo->name,
289 : p,
290 : namelen,
291 : 0);
292 9257 : if (ret == (size_t)-1) {
293 0 : return pdata_end - base;
294 : }
295 :
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. */
300 :
301 9257 : *p_last_name_raw = data_blob(NULL, namelen + 2);
302 9257 : memcpy(p_last_name_raw->data, p, namelen);
303 9257 : SSVAL(p_last_name_raw->data, namelen, 0);
304 :
305 9257 : return calc_next_entry_offset(base, pdata_end);
306 : }
307 : }
308 :
309 0 : DEBUG(1,("Unknown long filename format %d\n",level));
310 0 : return calc_next_entry_offset(base, pdata_end);
311 : }
312 :
313 : /****************************************************************************
314 : Interpret a short filename structure.
315 : The length of the structure is returned.
316 : ****************************************************************************/
317 :
318 131 : static bool interpret_short_filename(TALLOC_CTX *ctx,
319 : struct cli_state *cli,
320 : char *p,
321 : struct file_info *finfo)
322 : {
323 0 : size_t ret;
324 131 : ZERO_STRUCTP(finfo);
325 :
326 131 : finfo->attr = CVAL(p,21);
327 :
328 : /* We don't get birth time. */
329 131 : finfo->btime_ts.tv_sec = 0;
330 131 : finfo->btime_ts.tv_nsec = 0;
331 : /* this date is converted to GMT by make_unix_date */
332 131 : finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
333 131 : finfo->ctime_ts.tv_nsec = 0;
334 131 : finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
335 131 : finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
336 131 : finfo->size = IVAL(p,26);
337 131 : ret = pull_string_talloc(ctx,
338 : NULL,
339 : 0,
340 : &finfo->name,
341 131 : p+30,
342 : 12,
343 : STR_ASCII);
344 131 : if (ret == (size_t)-1) {
345 0 : return false;
346 : }
347 :
348 131 : if (finfo->name) {
349 131 : finfo->short_name = talloc_strdup(ctx, finfo->name);
350 131 : if (finfo->short_name == NULL) {
351 0 : return false;
352 : }
353 : }
354 131 : return true;
355 : }
356 :
357 : struct cli_list_old_state {
358 : struct tevent_context *ev;
359 : struct cli_state *cli;
360 : uint16_t vwv[2];
361 : char *mask;
362 : int num_asked;
363 : uint32_t attribute;
364 : uint8_t search_status[23];
365 : bool first;
366 : bool done;
367 : uint8_t *dirlist;
368 : };
369 :
370 : static void cli_list_old_done(struct tevent_req *subreq);
371 :
372 2 : static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
373 : struct tevent_context *ev,
374 : struct cli_state *cli,
375 : const char *mask,
376 : uint32_t attribute)
377 : {
378 0 : struct tevent_req *req, *subreq;
379 0 : struct cli_list_old_state *state;
380 0 : uint8_t *bytes;
381 0 : static const uint16_t zero = 0;
382 0 : uint32_t usable_space;
383 :
384 2 : req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
385 2 : if (req == NULL) {
386 0 : return NULL;
387 : }
388 2 : state->ev = ev;
389 2 : state->cli = cli;
390 2 : state->attribute = attribute;
391 2 : state->first = true;
392 2 : state->mask = talloc_strdup(state, mask);
393 2 : if (tevent_req_nomem(state->mask, req)) {
394 0 : return tevent_req_post(req, ev);
395 : }
396 2 : state->mask = smb1_dfs_share_path(state, cli, state->mask);
397 2 : if (tevent_req_nomem(state->mask, req)) {
398 0 : return tevent_req_post(req, ev);
399 : }
400 2 : usable_space = cli_state_available_size(cli, 100);
401 2 : state->num_asked = usable_space / DIR_STRUCT_SIZE;
402 :
403 2 : SSVAL(state->vwv + 0, 0, state->num_asked);
404 2 : SSVAL(state->vwv + 1, 0, state->attribute);
405 :
406 2 : bytes = talloc_array(state, uint8_t, 1);
407 2 : if (tevent_req_nomem(bytes, req)) {
408 0 : return tevent_req_post(req, ev);
409 : }
410 2 : bytes[0] = 4;
411 2 : bytes = smb_bytes_push_str(bytes,
412 2 : smbXcli_conn_use_unicode(cli->conn),
413 2 : state->mask,
414 2 : strlen(state->mask)+1,
415 : NULL);
416 :
417 2 : bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
418 2 : if (tevent_req_nomem(bytes, req)) {
419 0 : return tevent_req_post(req, ev);
420 : }
421 :
422 2 : subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
423 2 : 2, state->vwv, talloc_get_size(bytes), bytes);
424 2 : if (tevent_req_nomem(subreq, req)) {
425 0 : return tevent_req_post(req, ev);
426 : }
427 2 : tevent_req_set_callback(subreq, cli_list_old_done, req);
428 2 : return req;
429 : }
430 :
431 6 : static void cli_list_old_done(struct tevent_req *subreq)
432 : {
433 6 : struct tevent_req *req = tevent_req_callback_data(
434 : subreq, struct tevent_req);
435 6 : struct cli_list_old_state *state = tevent_req_data(
436 : req, struct cli_list_old_state);
437 0 : NTSTATUS status;
438 0 : uint8_t cmd;
439 0 : uint8_t wct;
440 0 : uint16_t *vwv;
441 0 : uint32_t num_bytes;
442 0 : uint8_t *bytes;
443 0 : uint16_t received;
444 0 : size_t dirlist_len;
445 0 : uint8_t *tmp;
446 :
447 6 : status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
448 : &bytes);
449 6 : if (!NT_STATUS_IS_OK(status)
450 0 : && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
451 0 : && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
452 0 : TALLOC_FREE(subreq);
453 0 : tevent_req_nterror(req, status);
454 0 : return;
455 : }
456 6 : if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
457 6 : || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
458 0 : received = 0;
459 : } else {
460 6 : if (wct < 1) {
461 0 : TALLOC_FREE(subreq);
462 0 : tevent_req_nterror(
463 : req, NT_STATUS_INVALID_NETWORK_RESPONSE);
464 0 : return;
465 : }
466 6 : received = SVAL(vwv + 0, 0);
467 : }
468 :
469 6 : if (received > 0) {
470 : /*
471 : * I don't think this can wrap. received is
472 : * initialized from a 16-bit value.
473 : */
474 2 : if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
475 0 : TALLOC_FREE(subreq);
476 0 : tevent_req_nterror(
477 : req, NT_STATUS_INVALID_NETWORK_RESPONSE);
478 0 : return;
479 : }
480 :
481 2 : dirlist_len = talloc_get_size(state->dirlist);
482 :
483 2 : tmp = talloc_realloc(
484 : state, state->dirlist, uint8_t,
485 : dirlist_len + received * DIR_STRUCT_SIZE);
486 2 : if (tevent_req_nomem(tmp, req)) {
487 0 : return;
488 : }
489 2 : state->dirlist = tmp;
490 2 : memcpy(state->dirlist + dirlist_len, bytes + 3,
491 2 : received * DIR_STRUCT_SIZE);
492 :
493 2 : SSVAL(state->search_status, 0, 21);
494 2 : memcpy(state->search_status + 2,
495 2 : bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
496 2 : cmd = SMBsearch;
497 : } else {
498 4 : if (state->first || state->done) {
499 2 : tevent_req_done(req);
500 2 : return;
501 : }
502 2 : state->done = true;
503 2 : state->num_asked = 0;
504 2 : cmd = SMBfclose;
505 : }
506 4 : TALLOC_FREE(subreq);
507 :
508 4 : state->first = false;
509 :
510 4 : SSVAL(state->vwv + 0, 0, state->num_asked);
511 4 : SSVAL(state->vwv + 1, 0, state->attribute);
512 :
513 4 : bytes = talloc_array(state, uint8_t, 1);
514 4 : if (tevent_req_nomem(bytes, req)) {
515 0 : return;
516 : }
517 4 : bytes[0] = 4;
518 4 : bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
519 : 1, NULL);
520 4 : bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
521 : sizeof(state->search_status));
522 4 : if (tevent_req_nomem(bytes, req)) {
523 0 : return;
524 : }
525 8 : subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
526 4 : 2, state->vwv, talloc_get_size(bytes), bytes);
527 4 : if (tevent_req_nomem(subreq, req)) {
528 0 : return;
529 : }
530 4 : tevent_req_set_callback(subreq, cli_list_old_done, req);
531 : }
532 :
533 4 : static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
534 : struct file_info **pfinfo)
535 : {
536 4 : struct cli_list_old_state *state = tevent_req_data(
537 : req, struct cli_list_old_state);
538 0 : NTSTATUS status;
539 0 : size_t i, num_received;
540 0 : struct file_info *finfo;
541 :
542 4 : if (tevent_req_is_nterror(req, &status)) {
543 0 : return status;
544 : }
545 :
546 4 : if (state->dirlist == NULL) {
547 2 : *pfinfo = NULL;
548 2 : return NT_STATUS_OK;
549 : }
550 :
551 2 : num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
552 :
553 2 : finfo = talloc_array(mem_ctx, struct file_info, num_received);
554 2 : if (finfo == NULL) {
555 0 : return NT_STATUS_NO_MEMORY;
556 : }
557 :
558 133 : for (i=0; i<num_received; i++) {
559 131 : if (!interpret_short_filename(
560 : finfo, state->cli,
561 131 : (char *)state->dirlist + i * DIR_STRUCT_SIZE,
562 131 : &finfo[i])) {
563 0 : TALLOC_FREE(finfo);
564 0 : return NT_STATUS_NO_MEMORY;
565 : }
566 131 : if (finfo->name == NULL) {
567 0 : TALLOC_FREE(finfo);
568 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
569 : }
570 131 : status = is_bad_finfo_name(state->cli, finfo);
571 131 : if (!NT_STATUS_IS_OK(status)) {
572 0 : smbXcli_conn_disconnect(state->cli->conn, status);
573 0 : TALLOC_FREE(finfo);
574 0 : return status;
575 : }
576 : }
577 2 : TALLOC_FREE(state->dirlist);
578 2 : *pfinfo = finfo;
579 2 : return NT_STATUS_OK;
580 : }
581 :
582 0 : NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
583 : uint32_t attribute,
584 : NTSTATUS (*fn)(struct file_info *,
585 : const char *, void *), void *state)
586 : {
587 0 : TALLOC_CTX *frame = talloc_stackframe();
588 0 : struct tevent_context *ev;
589 0 : struct tevent_req *req;
590 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
591 0 : struct file_info *finfo = NULL;
592 0 : size_t i, num_finfo;
593 :
594 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
595 : /*
596 : * Can't use sync call while an async call is in flight
597 : */
598 0 : status = NT_STATUS_INVALID_PARAMETER;
599 0 : goto fail;
600 : }
601 0 : ev = samba_tevent_context_init(frame);
602 0 : if (ev == NULL) {
603 0 : goto fail;
604 : }
605 0 : req = cli_list_old_send(frame, ev, cli, mask, attribute);
606 0 : if (req == NULL) {
607 0 : goto fail;
608 : }
609 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
610 0 : goto fail;
611 : }
612 0 : status = cli_list_old_recv(req, frame, &finfo);
613 0 : if (!NT_STATUS_IS_OK(status)) {
614 0 : goto fail;
615 : }
616 0 : num_finfo = talloc_array_length(finfo);
617 0 : for (i=0; i<num_finfo; i++) {
618 0 : status = fn(&finfo[i], mask, state);
619 0 : if (!NT_STATUS_IS_OK(status)) {
620 0 : goto fail;
621 : }
622 : }
623 0 : fail:
624 0 : TALLOC_FREE(frame);
625 0 : return status;
626 : }
627 :
628 : struct cli_list_trans_state {
629 : struct tevent_context *ev;
630 : struct cli_state *cli;
631 : char *mask;
632 : uint32_t attribute;
633 : uint16_t info_level;
634 :
635 : int loop_count;
636 : int total_received;
637 : uint16_t max_matches;
638 : bool first;
639 :
640 : int ff_eos;
641 : int ff_dir_handle;
642 :
643 : uint16_t setup[1];
644 : uint8_t *param;
645 :
646 : struct file_info *finfo;
647 : };
648 :
649 : static void cli_list_trans_done(struct tevent_req *subreq);
650 :
651 2229 : static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
652 : struct tevent_context *ev,
653 : struct cli_state *cli,
654 : const char *mask,
655 : uint32_t attribute,
656 : uint16_t info_level)
657 : {
658 0 : struct tevent_req *req, *subreq;
659 0 : struct cli_list_trans_state *state;
660 0 : size_t param_len;
661 2229 : uint16_t additional_flags2 = 0;
662 :
663 2229 : req = tevent_req_create(mem_ctx, &state,
664 : struct cli_list_trans_state);
665 2229 : if (req == NULL) {
666 0 : return NULL;
667 : }
668 2229 : state->ev = ev;
669 2229 : state->cli = cli;
670 2229 : state->mask = smb1_dfs_share_path(state, cli, mask);
671 2229 : if (tevent_req_nomem(state->mask, req)) {
672 0 : return tevent_req_post(req, ev);
673 : }
674 2229 : state->attribute = attribute;
675 2229 : state->info_level = info_level;
676 2229 : state->loop_count = 0;
677 2229 : state->first = true;
678 :
679 2229 : state->max_matches = 1366; /* Match W2k */
680 :
681 2229 : SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
682 :
683 2229 : state->param = talloc_array(state, uint8_t, 12);
684 2229 : if (tevent_req_nomem(state->param, req)) {
685 0 : return tevent_req_post(req, ev);
686 : }
687 :
688 2229 : SSVAL(state->param, 0, state->attribute);
689 2229 : SSVAL(state->param, 2, state->max_matches);
690 2229 : SSVAL(state->param, 4,
691 : FLAG_TRANS2_FIND_REQUIRE_RESUME
692 : |FLAG_TRANS2_FIND_CLOSE_IF_END
693 : |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
694 2229 : SSVAL(state->param, 6, state->info_level);
695 2229 : SIVAL(state->param, 8, 0);
696 :
697 2229 : state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
698 2229 : state->mask, strlen(state->mask)+1,
699 : NULL);
700 2229 : if (tevent_req_nomem(state->param, req)) {
701 0 : return tevent_req_post(req, ev);
702 : }
703 :
704 2229 : if (clistr_is_previous_version_path(state->mask)) {
705 68 : additional_flags2 = FLAGS2_REPARSE_PATH;
706 : }
707 :
708 2229 : param_len = talloc_get_size(state->param);
709 :
710 2229 : subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
711 : SMBtrans2, NULL, -1, 0, 0,
712 2229 : state->setup, 1, 0,
713 2229 : state->param, param_len, 10,
714 : NULL, 0, CLI_BUFFER_SIZE);
715 2229 : if (tevent_req_nomem(subreq, req)) {
716 0 : return tevent_req_post(req, ev);
717 : }
718 2229 : tevent_req_set_callback(subreq, cli_list_trans_done, req);
719 2229 : return req;
720 : }
721 :
722 2229 : static void cli_list_trans_done(struct tevent_req *subreq)
723 : {
724 2229 : struct tevent_req *req = tevent_req_callback_data(
725 : subreq, struct tevent_req);
726 2229 : struct cli_list_trans_state *state = tevent_req_data(
727 : req, struct cli_list_trans_state);
728 0 : NTSTATUS status;
729 0 : uint8_t *param;
730 0 : uint32_t num_param;
731 0 : uint8_t *data;
732 0 : char *data_end;
733 0 : uint32_t num_data;
734 0 : uint32_t min_param;
735 0 : struct file_info *tmp;
736 0 : size_t old_num_finfo;
737 0 : uint16_t recv_flags2;
738 0 : int ff_searchcount;
739 0 : bool ff_eos;
740 0 : char *p, *p2;
741 2229 : uint32_t resume_key = 0;
742 0 : int i;
743 0 : DATA_BLOB last_name_raw;
744 2229 : struct file_info *finfo = NULL;
745 0 : size_t param_len;
746 2229 : uint16_t additional_flags2 = 0;
747 :
748 2229 : min_param = (state->first ? 6 : 4);
749 :
750 2229 : status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
751 : NULL, 0, NULL,
752 : ¶m, min_param, &num_param,
753 : &data, 0, &num_data);
754 2229 : TALLOC_FREE(subreq);
755 2229 : if (!NT_STATUS_IS_OK(status)) {
756 : /*
757 : * TODO: retry, OS/2 nofiles
758 : */
759 350 : tevent_req_nterror(req, status);
760 2229 : return;
761 : }
762 :
763 1879 : if (state->first) {
764 1879 : state->ff_dir_handle = SVAL(param, 0);
765 1879 : ff_searchcount = SVAL(param, 2);
766 1879 : ff_eos = SVAL(param, 4) != 0;
767 : } else {
768 0 : ff_searchcount = SVAL(param, 0);
769 0 : ff_eos = SVAL(param, 2) != 0;
770 : }
771 :
772 1879 : old_num_finfo = talloc_array_length(state->finfo);
773 :
774 1879 : tmp = talloc_realloc(state, state->finfo, struct file_info,
775 : old_num_finfo + ff_searchcount);
776 1879 : if (tevent_req_nomem(tmp, req)) {
777 0 : return;
778 : }
779 1879 : state->finfo = tmp;
780 :
781 1879 : p2 = p = (char *)data;
782 1879 : data_end = (char *)data + num_data;
783 1879 : last_name_raw = data_blob_null;
784 :
785 11134 : for (i=0; i<ff_searchcount; i++) {
786 9257 : if (p2 >= data_end) {
787 0 : ff_eos = true;
788 0 : break;
789 : }
790 9257 : if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
791 9257 : && (i == ff_searchcount-1)) {
792 : /* Last entry - fixup the last offset length. */
793 1879 : SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
794 : }
795 :
796 9257 : data_blob_free(&last_name_raw);
797 :
798 9257 : finfo = &state->finfo[old_num_finfo + i];
799 :
800 18514 : p2 += interpret_long_filename(
801 9257 : state->finfo, /* Stick fname to the array as such */
802 9257 : state->cli, state->info_level,
803 : (char *)data, recv_flags2, p2,
804 : data_end, finfo, &resume_key, &last_name_raw);
805 :
806 9257 : if (finfo->name == NULL) {
807 2 : DEBUG(1, ("cli_list: Error: unable to parse name from "
808 : "info level %d\n", state->info_level));
809 2 : tevent_req_nterror(req,
810 : NT_STATUS_INVALID_NETWORK_RESPONSE);
811 2 : return;
812 : }
813 :
814 9255 : status = is_bad_finfo_name(state->cli, finfo);
815 9255 : if (!NT_STATUS_IS_OK(status)) {
816 0 : smbXcli_conn_disconnect(state->cli->conn, status);
817 0 : tevent_req_nterror(req, status);
818 0 : return;
819 : }
820 :
821 9255 : if (!state->first && (state->mask[0] != '\0') &&
822 0 : strcsequal(finfo->name, state->mask)) {
823 0 : DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
824 : "already been seen?\n", finfo->name));
825 0 : ff_eos = true;
826 0 : break;
827 : }
828 : }
829 :
830 1877 : if (ff_searchcount == 0) {
831 0 : ff_eos = true;
832 : }
833 :
834 1877 : TALLOC_FREE(param);
835 1877 : TALLOC_FREE(data);
836 :
837 : /*
838 : * Shrink state->finfo to the real length we received
839 : */
840 1877 : tmp = talloc_realloc(state, state->finfo, struct file_info,
841 : old_num_finfo + i);
842 1877 : if (tevent_req_nomem(tmp, req)) {
843 0 : return;
844 : }
845 1877 : state->finfo = tmp;
846 :
847 1877 : state->first = false;
848 :
849 1877 : if (ff_eos) {
850 1877 : data_blob_free(&last_name_raw);
851 1877 : tevent_req_done(req);
852 1877 : return;
853 : }
854 :
855 0 : TALLOC_FREE(state->mask);
856 0 : state->mask = talloc_strdup(state, finfo->name);
857 0 : if (tevent_req_nomem(state->mask, req)) {
858 0 : return;
859 : }
860 :
861 0 : SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
862 :
863 0 : param = talloc_realloc(state, state->param, uint8_t, 12);
864 0 : if (tevent_req_nomem(param, req)) {
865 0 : return;
866 : }
867 0 : state->param = param;
868 :
869 0 : SSVAL(param, 0, state->ff_dir_handle);
870 0 : SSVAL(param, 2, state->max_matches); /* max count */
871 0 : SSVAL(param, 4, state->info_level);
872 : /*
873 : * For W2K servers serving out FAT filesystems we *must* set
874 : * the resume key. If it's not FAT then it's returned as zero.
875 : */
876 0 : SIVAL(param, 6, resume_key); /* ff_resume_key */
877 : /*
878 : * NB. *DON'T* use continue here. If you do it seems that W2K
879 : * and brethren can miss filenames. Use last filename
880 : * continue instead. JRA
881 : */
882 0 : SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
883 : |FLAG_TRANS2_FIND_CLOSE_IF_END
884 : |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
885 0 : if (last_name_raw.length) {
886 0 : state->param = trans2_bytes_push_bytes(state->param,
887 0 : last_name_raw.data,
888 : last_name_raw.length);
889 0 : if (tevent_req_nomem(state->param, req)) {
890 0 : return;
891 : }
892 0 : data_blob_free(&last_name_raw);
893 : } else {
894 0 : state->param = trans2_bytes_push_str(state->param,
895 0 : smbXcli_conn_use_unicode(state->cli->conn),
896 0 : state->mask,
897 0 : strlen(state->mask)+1,
898 : NULL);
899 0 : if (tevent_req_nomem(state->param, req)) {
900 0 : return;
901 : }
902 : }
903 0 : param_len = talloc_get_size(state->param);
904 :
905 0 : if (clistr_is_previous_version_path(state->mask)) {
906 0 : additional_flags2 = FLAGS2_REPARSE_PATH;
907 : }
908 :
909 0 : subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
910 : SMBtrans2, NULL, -1, 0, 0,
911 0 : state->setup, 1, 0,
912 : state->param, param_len, 10,
913 : NULL, 0, CLI_BUFFER_SIZE);
914 0 : if (tevent_req_nomem(subreq, req)) {
915 0 : return;
916 : }
917 0 : tevent_req_set_callback(subreq, cli_list_trans_done, req);
918 : }
919 :
920 4106 : static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
921 : TALLOC_CTX *mem_ctx,
922 : struct file_info **finfo)
923 : {
924 4106 : struct cli_list_trans_state *state = tevent_req_data(
925 : req, struct cli_list_trans_state);
926 0 : NTSTATUS status;
927 :
928 4106 : if (tevent_req_is_nterror(req, &status)) {
929 352 : return status;
930 : }
931 3754 : *finfo = talloc_move(mem_ctx, &state->finfo);
932 3754 : return NT_STATUS_OK;
933 : }
934 :
935 : struct cli_list_state {
936 : struct tevent_context *ev;
937 : struct tevent_req *subreq;
938 : NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
939 : struct file_info **finfo);
940 : struct file_info *finfo;
941 : size_t num_received;
942 : };
943 :
944 : static void cli_list_done(struct tevent_req *subreq);
945 :
946 11057 : struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
947 : struct tevent_context *ev,
948 : struct cli_state *cli,
949 : const char *mask,
950 : uint32_t attribute,
951 : uint16_t info_level)
952 : {
953 11057 : struct tevent_req *req = NULL;
954 0 : struct cli_list_state *state;
955 11057 : enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
956 :
957 11057 : req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
958 11057 : if (req == NULL) {
959 0 : return NULL;
960 : }
961 11057 : state->ev = ev;
962 :
963 11057 : if (proto >= PROTOCOL_SMB2_02) {
964 8826 : state->subreq = cli_smb2_list_send(state, ev, cli, mask,
965 : info_level);
966 8826 : state->recv_fn = cli_smb2_list_recv;
967 2231 : } else if (proto >= PROTOCOL_LANMAN2) {
968 2229 : state->subreq = cli_list_trans_send(
969 : state, ev, cli, mask, attribute, info_level);
970 2229 : state->recv_fn = cli_list_trans_recv;
971 : } else {
972 2 : state->subreq = cli_list_old_send(
973 : state, ev, cli, mask, attribute);
974 2 : state->recv_fn = cli_list_old_recv;
975 : }
976 11057 : if (tevent_req_nomem(state->subreq, req)) {
977 0 : return tevent_req_post(req, ev);
978 : }
979 11057 : tevent_req_set_callback(state->subreq, cli_list_done, req);
980 11057 : return req;
981 : }
982 :
983 27920 : static void cli_list_done(struct tevent_req *subreq)
984 : {
985 27920 : struct tevent_req *req = tevent_req_callback_data(
986 : subreq, struct tevent_req);
987 27920 : struct cli_list_state *state = tevent_req_data(
988 : req, struct cli_list_state);
989 0 : NTSTATUS status;
990 :
991 27920 : SMB_ASSERT(subreq == state->subreq);
992 :
993 : /*
994 : * We don't want to be called by the lowerlevel routines
995 : * from within state->recv_fn()
996 : */
997 27920 : tevent_req_set_callback(subreq, NULL, NULL);
998 :
999 27920 : status = state->recv_fn(subreq, state, &state->finfo);
1000 27920 : if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1001 : /* We'll get back here */
1002 8546 : tevent_req_set_callback(subreq, cli_list_done, req);
1003 8546 : return;
1004 : }
1005 :
1006 19374 : if (tevent_req_nterror(req, status)) {
1007 9176 : return;
1008 : }
1009 10198 : tevent_req_notify_callback(req);
1010 : }
1011 :
1012 86643 : NTSTATUS cli_list_recv(
1013 : struct tevent_req *req,
1014 : TALLOC_CTX *mem_ctx,
1015 : struct file_info **pfinfo)
1016 : {
1017 86643 : struct cli_list_state *state = tevent_req_data(
1018 : req, struct cli_list_state);
1019 0 : size_t num_results;
1020 86643 : struct file_info *finfo = NULL;
1021 0 : NTSTATUS status;
1022 0 : bool in_progress;
1023 :
1024 86643 : in_progress = tevent_req_is_in_progress(req);
1025 :
1026 86643 : if (!in_progress) {
1027 11057 : if (!tevent_req_is_nterror(req, &status)) {
1028 0 : status = NT_STATUS_NO_MORE_FILES;
1029 : }
1030 11057 : return status;
1031 : }
1032 :
1033 75586 : if (state->finfo == NULL) {
1034 57885 : status = state->recv_fn(state->subreq, state, &state->finfo);
1035 :
1036 57885 : if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1037 8317 : tevent_req_set_callback(
1038 : state->subreq, cli_list_done, req);
1039 8317 : return NT_STATUS_RETRY;
1040 : }
1041 :
1042 49568 : if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1043 1879 : status = NT_STATUS_NO_MORE_FILES;
1044 : }
1045 :
1046 49568 : if (tevent_req_nterror(req, status)) {
1047 1881 : return status;
1048 : }
1049 :
1050 47687 : state->num_received = 0;
1051 : }
1052 :
1053 65388 : num_results = talloc_array_length(state->finfo);
1054 :
1055 65388 : if (num_results == 1) {
1056 56696 : finfo = talloc_move(mem_ctx, &state->finfo);
1057 : } else {
1058 8692 : struct file_info *src_finfo =
1059 8692 : &state->finfo[state->num_received];
1060 :
1061 8692 : finfo = talloc(mem_ctx, struct file_info);
1062 8692 : if (finfo == NULL) {
1063 0 : return NT_STATUS_NO_MEMORY;
1064 : }
1065 8692 : *finfo = *src_finfo;
1066 8692 : finfo->name = talloc_move(finfo, &src_finfo->name);
1067 8692 : finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1068 : }
1069 :
1070 65388 : state->num_received += 1;
1071 :
1072 65388 : if (state->num_received == num_results) {
1073 57885 : TALLOC_FREE(state->finfo);
1074 : }
1075 :
1076 65388 : tevent_req_defer_callback(req, state->ev);
1077 65388 : tevent_req_notify_callback(req);
1078 :
1079 65388 : *pfinfo = finfo;
1080 65388 : return NT_STATUS_OK;
1081 : }
1082 :
1083 : struct cli_list_sync_state {
1084 : const char *mask;
1085 : uint32_t attribute;
1086 : NTSTATUS (*fn)(struct file_info *finfo,
1087 : const char *mask,
1088 : void *private_data);
1089 : void *private_data;
1090 : NTSTATUS status;
1091 : bool processed_file;
1092 : };
1093 :
1094 66848 : static void cli_list_sync_cb(struct tevent_req *subreq)
1095 : {
1096 0 : struct cli_list_sync_state *state =
1097 66848 : tevent_req_callback_data_void(subreq);
1098 0 : struct file_info *finfo;
1099 0 : bool ok;
1100 :
1101 66848 : state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1102 : /* No TALLOC_FREE(subreq), we get here more than once */
1103 :
1104 66848 : if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1105 : /*
1106 : * The lowlevel SMB call was rearmed, we'll get back
1107 : * here when it's done.
1108 : */
1109 4854 : state->status = NT_STATUS_OK;
1110 4854 : return;
1111 : }
1112 :
1113 61994 : if (!NT_STATUS_IS_OK(state->status)) {
1114 9473 : return;
1115 : }
1116 :
1117 52521 : ok = dir_check_ftype(finfo->attr, state->attribute);
1118 52521 : if (!ok) {
1119 : /*
1120 : * Only process if attributes match. On SMB1 server
1121 : * does this, so on SMB2 we need to emulate in the
1122 : * client.
1123 : *
1124 : * https://bugzilla.samba.org/show_bug.cgi?id=10260
1125 : */
1126 0 : return;
1127 : }
1128 :
1129 52521 : state->status = state->fn(finfo, state->mask, state->private_data);
1130 :
1131 52521 : state->processed_file = true;
1132 :
1133 52521 : TALLOC_FREE(finfo);
1134 : }
1135 :
1136 7592 : NTSTATUS cli_list(struct cli_state *cli,
1137 : const char *mask,
1138 : uint32_t attribute,
1139 : NTSTATUS (*fn)(struct file_info *finfo,
1140 : const char *mask,
1141 : void *private_data),
1142 : void *private_data)
1143 : {
1144 7592 : TALLOC_CTX *frame = NULL;
1145 7592 : struct cli_list_sync_state state = {
1146 : .mask = mask,
1147 : .attribute = attribute,
1148 : .fn = fn,
1149 : .private_data = private_data,
1150 : };
1151 0 : struct tevent_context *ev;
1152 0 : struct tevent_req *req;
1153 7592 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1154 0 : uint16_t info_level;
1155 7592 : enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1156 :
1157 7592 : frame = talloc_stackframe();
1158 :
1159 7592 : if (smbXcli_conn_has_async_calls(cli->conn)) {
1160 : /*
1161 : * Can't use sync call while an async call is in flight
1162 : */
1163 0 : status = NT_STATUS_INVALID_PARAMETER;
1164 0 : goto fail;
1165 : }
1166 7592 : ev = samba_tevent_context_init(frame);
1167 7592 : if (ev == NULL) {
1168 0 : goto fail;
1169 : }
1170 :
1171 7592 : if (proto >= PROTOCOL_SMB2_02) {
1172 5361 : info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
1173 : } else {
1174 2231 : info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1175 : ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1176 : }
1177 :
1178 7592 : req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1179 7592 : if (req == NULL) {
1180 0 : goto fail;
1181 : }
1182 7592 : tevent_req_set_callback(req, cli_list_sync_cb, &state);
1183 :
1184 7592 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1185 0 : goto fail;
1186 : }
1187 :
1188 7592 : status = state.status;
1189 :
1190 7592 : if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1191 6958 : status = NT_STATUS_OK;
1192 : }
1193 :
1194 7592 : if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1195 225 : status = NT_STATUS_NO_SUCH_FILE;
1196 : }
1197 :
1198 7367 : fail:
1199 7592 : TALLOC_FREE(frame);
1200 7592 : return status;
1201 : }
|