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