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