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 56166 : static NTSTATUS is_bad_name(bool windows_names, const char *name)
32 : {
33 56166 : const char *bad_name_p = NULL;
34 :
35 56166 : bad_name_p = strchr(name, '/');
36 56166 : 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 56166 : if (windows_names) {
44 55960 : bad_name_p = strchr(name, '\\');
45 55960 : 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 56166 : 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 41099 : NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
62 : const struct file_info *finfo)
63 : {
64 41099 : NTSTATUS status = NT_STATUS_OK;
65 41099 : bool windows_names = true;
66 :
67 41099 : if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
68 206 : windows_names = false;
69 : }
70 41099 : if (finfo->name != NULL) {
71 41099 : status = is_bad_name(windows_names, finfo->name);
72 41099 : if (!NT_STATUS_IS_OK(status)) {
73 0 : DBG_ERR("bad finfo->name\n");
74 0 : return status;
75 : }
76 : }
77 41099 : if (finfo->short_name != NULL) {
78 15067 : status = is_bad_name(windows_names, finfo->short_name);
79 15067 : if (!NT_STATUS_IS_OK(status)) {
80 0 : DBG_ERR("bad finfo->short_name\n");
81 0 : return status;
82 : }
83 : }
84 41099 : return NT_STATUS_OK;
85 : }
86 :
87 : /****************************************************************************
88 : Calculate a safe next_entry_offset.
89 : ****************************************************************************/
90 :
91 7103 : static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
92 : {
93 7103 : size_t next_entry_offset = (size_t)IVAL(base,0);
94 :
95 14206 : if (next_entry_offset == 0 ||
96 14206 : base + next_entry_offset < base ||
97 7103 : base + next_entry_offset > pdata_end) {
98 0 : next_entry_offset = pdata_end - base;
99 : }
100 7103 : 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 7103 : 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 7103 : const char *base = p;
125 :
126 7103 : data_blob_free(p_last_name_raw);
127 :
128 7103 : if (p_resume_key) {
129 7103 : *p_resume_key = 0;
130 : }
131 7103 : ZERO_STRUCTP(finfo);
132 :
133 7103 : 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 7103 : case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
235 : {
236 : size_t namelen, slen;
237 :
238 7103 : if (pdata_end - base < 94) {
239 0 : return pdata_end - base;
240 : }
241 :
242 7103 : p += 4; /* next entry offset */
243 :
244 7103 : if (p_resume_key) {
245 7103 : *p_resume_key = IVAL(p,0);
246 : }
247 7103 : p += 4; /* fileindex */
248 :
249 : /* Offset zero is "create time", not "change time". */
250 7103 : p += 8;
251 7103 : finfo->atime_ts = interpret_long_date(p);
252 7103 : p += 8;
253 7103 : finfo->mtime_ts = interpret_long_date(p);
254 7103 : p += 8;
255 7103 : finfo->ctime_ts = interpret_long_date(p);
256 7103 : p += 8;
257 7103 : finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
258 7103 : p += 8;
259 7103 : p += 8; /* alloc size */
260 7103 : finfo->attr = IVAL(p,0);
261 7103 : p += 4;
262 7103 : namelen = IVAL(p,0);
263 7103 : p += 4;
264 7103 : p += 4; /* EA size */
265 7103 : slen = CVAL(p, 0);
266 7103 : if (slen > 24) {
267 : /* Bad short name length. */
268 0 : return pdata_end - base;
269 : }
270 7103 : p += 2;
271 7103 : ret = pull_string_talloc(ctx,
272 : base_ptr,
273 : recv_flags2,
274 : &finfo->short_name,
275 : p,
276 : slen,
277 : STR_UNICODE);
278 7103 : if (ret == (size_t)-1) {
279 0 : return pdata_end - base;
280 : }
281 7103 : p += 24; /* short name? */
282 7103 : if (p + namelen < p || p + namelen > pdata_end) {
283 0 : return pdata_end - base;
284 : }
285 7103 : ret = pull_string_talloc(ctx,
286 : base_ptr,
287 : recv_flags2,
288 : &finfo->name,
289 : p,
290 : namelen,
291 : 0);
292 7103 : 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 7103 : if (p_last_name_raw) {
302 7103 : *p_last_name_raw = data_blob(NULL, namelen+2);
303 7103 : memcpy(p_last_name_raw->data, p, namelen);
304 7103 : SSVAL(p_last_name_raw->data, namelen, 0);
305 : }
306 7103 : 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 : num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
541 :
542 0 : finfo = talloc_array(mem_ctx, struct file_info, num_received);
543 0 : if (finfo == NULL) {
544 0 : return NT_STATUS_NO_MEMORY;
545 : }
546 :
547 0 : for (i=0; i<num_received; i++) {
548 0 : if (!interpret_short_filename(
549 : finfo, state->cli,
550 0 : (char *)state->dirlist + i * DIR_STRUCT_SIZE,
551 0 : &finfo[i])) {
552 0 : TALLOC_FREE(finfo);
553 0 : return NT_STATUS_NO_MEMORY;
554 : }
555 0 : if (finfo->name == NULL) {
556 0 : TALLOC_FREE(finfo);
557 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
558 : }
559 0 : status = is_bad_finfo_name(state->cli, finfo);
560 0 : if (!NT_STATUS_IS_OK(status)) {
561 0 : smbXcli_conn_disconnect(state->cli->conn, status);
562 0 : TALLOC_FREE(finfo);
563 0 : return status;
564 : }
565 : }
566 0 : *pfinfo = finfo;
567 0 : return NT_STATUS_OK;
568 : }
569 :
570 0 : NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
571 : uint32_t attribute,
572 : NTSTATUS (*fn)(struct file_info *,
573 : const char *, void *), void *state)
574 : {
575 0 : TALLOC_CTX *frame = talloc_stackframe();
576 : struct tevent_context *ev;
577 : struct tevent_req *req;
578 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
579 0 : struct file_info *finfo = NULL;
580 : size_t i, num_finfo;
581 :
582 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
583 : /*
584 : * Can't use sync call while an async call is in flight
585 : */
586 0 : status = NT_STATUS_INVALID_PARAMETER;
587 0 : goto fail;
588 : }
589 0 : ev = samba_tevent_context_init(frame);
590 0 : if (ev == NULL) {
591 0 : goto fail;
592 : }
593 0 : req = cli_list_old_send(frame, ev, cli, mask, attribute);
594 0 : if (req == NULL) {
595 0 : goto fail;
596 : }
597 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
598 0 : goto fail;
599 : }
600 0 : status = cli_list_old_recv(req, frame, &finfo);
601 0 : if (!NT_STATUS_IS_OK(status)) {
602 0 : goto fail;
603 : }
604 0 : num_finfo = talloc_array_length(finfo);
605 0 : for (i=0; i<num_finfo; i++) {
606 0 : status = fn(&finfo[i], mask, state);
607 0 : if (!NT_STATUS_IS_OK(status)) {
608 0 : goto fail;
609 : }
610 : }
611 0 : fail:
612 0 : TALLOC_FREE(frame);
613 0 : return status;
614 : }
615 :
616 : struct cli_list_trans_state {
617 : struct tevent_context *ev;
618 : struct cli_state *cli;
619 : char *mask;
620 : uint32_t attribute;
621 : uint16_t info_level;
622 :
623 : int loop_count;
624 : int total_received;
625 : uint16_t max_matches;
626 : bool first;
627 :
628 : int ff_eos;
629 : int ff_dir_handle;
630 :
631 : uint16_t setup[1];
632 : uint8_t *param;
633 :
634 : struct file_info *finfo;
635 : };
636 :
637 : static void cli_list_trans_done(struct tevent_req *subreq);
638 :
639 1377 : static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
640 : struct tevent_context *ev,
641 : struct cli_state *cli,
642 : const char *mask,
643 : uint32_t attribute,
644 : uint16_t info_level)
645 : {
646 : struct tevent_req *req, *subreq;
647 : struct cli_list_trans_state *state;
648 : size_t param_len;
649 1377 : uint16_t additional_flags2 = 0;
650 :
651 1377 : req = tevent_req_create(mem_ctx, &state,
652 : struct cli_list_trans_state);
653 1377 : if (req == NULL) {
654 0 : return NULL;
655 : }
656 1377 : state->ev = ev;
657 1377 : state->cli = cli;
658 1377 : state->mask = talloc_strdup(state, mask);
659 1377 : if (tevent_req_nomem(state->mask, req)) {
660 0 : return tevent_req_post(req, ev);
661 : }
662 1377 : state->attribute = attribute;
663 1377 : state->info_level = info_level;
664 1377 : state->loop_count = 0;
665 1377 : state->first = true;
666 :
667 1377 : state->max_matches = 1366; /* Match W2k */
668 :
669 1377 : SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
670 :
671 1377 : state->param = talloc_array(state, uint8_t, 12);
672 1377 : if (tevent_req_nomem(state->param, req)) {
673 0 : return tevent_req_post(req, ev);
674 : }
675 :
676 1377 : SSVAL(state->param, 0, state->attribute);
677 1377 : SSVAL(state->param, 2, state->max_matches);
678 1377 : SSVAL(state->param, 4,
679 : FLAG_TRANS2_FIND_REQUIRE_RESUME
680 : |FLAG_TRANS2_FIND_CLOSE_IF_END
681 : |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
682 1377 : SSVAL(state->param, 6, state->info_level);
683 1377 : SIVAL(state->param, 8, 0);
684 :
685 2754 : state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
686 2754 : state->mask, strlen(state->mask)+1,
687 : NULL);
688 1377 : if (tevent_req_nomem(state->param, req)) {
689 0 : return tevent_req_post(req, ev);
690 : }
691 :
692 1377 : if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
693 68 : additional_flags2 = FLAGS2_REPARSE_PATH;
694 : }
695 :
696 1377 : param_len = talloc_get_size(state->param);
697 :
698 4131 : subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
699 : SMBtrans2, NULL, -1, 0, 0,
700 1377 : state->setup, 1, 0,
701 1377 : state->param, param_len, 10,
702 : NULL, 0, CLI_BUFFER_SIZE);
703 1377 : if (tevent_req_nomem(subreq, req)) {
704 0 : return tevent_req_post(req, ev);
705 : }
706 1377 : tevent_req_set_callback(subreq, cli_list_trans_done, req);
707 1377 : return req;
708 : }
709 :
710 1377 : static void cli_list_trans_done(struct tevent_req *subreq)
711 : {
712 1377 : struct tevent_req *req = tevent_req_callback_data(
713 : subreq, struct tevent_req);
714 1377 : struct cli_list_trans_state *state = tevent_req_data(
715 : req, struct cli_list_trans_state);
716 : NTSTATUS status;
717 : uint8_t *param;
718 : uint32_t num_param;
719 : uint8_t *data;
720 : char *data_end;
721 : uint32_t num_data;
722 : uint32_t min_param;
723 : struct file_info *tmp;
724 : size_t old_num_finfo;
725 : uint16_t recv_flags2;
726 : int ff_searchcount;
727 : bool ff_eos;
728 : char *p, *p2;
729 1377 : uint32_t resume_key = 0;
730 : int i;
731 : DATA_BLOB last_name_raw;
732 1377 : struct file_info *finfo = NULL;
733 : size_t param_len;
734 1377 : uint16_t additional_flags2 = 0;
735 :
736 1377 : min_param = (state->first ? 6 : 4);
737 :
738 1377 : status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
739 : NULL, 0, NULL,
740 : ¶m, min_param, &num_param,
741 : &data, 0, &num_data);
742 1377 : TALLOC_FREE(subreq);
743 1377 : if (!NT_STATUS_IS_OK(status)) {
744 : /*
745 : * TODO: retry, OS/2 nofiles
746 : */
747 79 : tevent_req_nterror(req, status);
748 79 : return;
749 : }
750 :
751 1298 : if (state->first) {
752 1298 : state->ff_dir_handle = SVAL(param, 0);
753 1298 : ff_searchcount = SVAL(param, 2);
754 1298 : ff_eos = SVAL(param, 4) != 0;
755 : } else {
756 0 : ff_searchcount = SVAL(param, 0);
757 0 : ff_eos = SVAL(param, 2) != 0;
758 : }
759 :
760 1298 : old_num_finfo = talloc_array_length(state->finfo);
761 :
762 1298 : tmp = talloc_realloc(state, state->finfo, struct file_info,
763 : old_num_finfo + ff_searchcount);
764 1298 : if (tevent_req_nomem(tmp, req)) {
765 0 : return;
766 : }
767 1298 : state->finfo = tmp;
768 :
769 1298 : p2 = p = (char *)data;
770 1298 : data_end = (char *)data + num_data;
771 1298 : last_name_raw = data_blob_null;
772 :
773 8399 : for (i=0; i<ff_searchcount; i++) {
774 7103 : if (p2 >= data_end) {
775 0 : ff_eos = true;
776 0 : break;
777 : }
778 7103 : if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
779 7103 : && (i == ff_searchcount-1)) {
780 : /* Last entry - fixup the last offset length. */
781 1298 : SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
782 : }
783 :
784 7103 : data_blob_free(&last_name_raw);
785 :
786 7103 : finfo = &state->finfo[old_num_finfo + i];
787 :
788 21309 : p2 += interpret_long_filename(
789 7103 : state->finfo, /* Stick fname to the array as such */
790 7103 : state->cli, state->info_level,
791 : (char *)data, recv_flags2, p2,
792 : data_end, finfo, &resume_key, &last_name_raw);
793 :
794 7103 : if (finfo->name == NULL) {
795 2 : DEBUG(1, ("cli_list: Error: unable to parse name from "
796 : "info level %d\n", state->info_level));
797 2 : tevent_req_nterror(req,
798 : NT_STATUS_INVALID_NETWORK_RESPONSE);
799 2 : return;
800 : }
801 :
802 7101 : status = is_bad_finfo_name(state->cli, finfo);
803 7101 : if (!NT_STATUS_IS_OK(status)) {
804 0 : smbXcli_conn_disconnect(state->cli->conn, status);
805 0 : tevent_req_nterror(req, status);
806 0 : return;
807 : }
808 :
809 7101 : if (!state->first && (state->mask[0] != '\0') &&
810 0 : strcsequal(finfo->name, state->mask)) {
811 0 : DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
812 : "already been seen?\n", finfo->name));
813 0 : ff_eos = true;
814 0 : break;
815 : }
816 : }
817 :
818 1296 : if (ff_searchcount == 0) {
819 0 : ff_eos = true;
820 : }
821 :
822 1296 : TALLOC_FREE(param);
823 1296 : TALLOC_FREE(data);
824 :
825 : /*
826 : * Shrink state->finfo to the real length we received
827 : */
828 1296 : tmp = talloc_realloc(state, state->finfo, struct file_info,
829 : old_num_finfo + i);
830 1296 : if (tevent_req_nomem(tmp, req)) {
831 0 : return;
832 : }
833 1296 : state->finfo = tmp;
834 :
835 1296 : state->first = false;
836 :
837 1296 : if (ff_eos) {
838 1296 : data_blob_free(&last_name_raw);
839 1296 : tevent_req_done(req);
840 1296 : return;
841 : }
842 :
843 0 : TALLOC_FREE(state->mask);
844 0 : state->mask = talloc_strdup(state, finfo->name);
845 0 : if (tevent_req_nomem(state->mask, req)) {
846 0 : return;
847 : }
848 :
849 0 : SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
850 :
851 0 : param = talloc_realloc(state, state->param, uint8_t, 12);
852 0 : if (tevent_req_nomem(param, req)) {
853 0 : return;
854 : }
855 0 : state->param = param;
856 :
857 0 : SSVAL(param, 0, state->ff_dir_handle);
858 0 : SSVAL(param, 2, state->max_matches); /* max count */
859 0 : SSVAL(param, 4, state->info_level);
860 : /*
861 : * For W2K servers serving out FAT filesystems we *must* set
862 : * the resume key. If it's not FAT then it's returned as zero.
863 : */
864 0 : SIVAL(param, 6, resume_key); /* ff_resume_key */
865 : /*
866 : * NB. *DON'T* use continue here. If you do it seems that W2K
867 : * and bretheren can miss filenames. Use last filename
868 : * continue instead. JRA
869 : */
870 0 : SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
871 : |FLAG_TRANS2_FIND_CLOSE_IF_END
872 : |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
873 0 : if (last_name_raw.length) {
874 0 : state->param = trans2_bytes_push_bytes(state->param,
875 0 : last_name_raw.data,
876 : last_name_raw.length);
877 0 : if (tevent_req_nomem(state->param, req)) {
878 0 : return;
879 : }
880 0 : data_blob_free(&last_name_raw);
881 : } else {
882 0 : state->param = trans2_bytes_push_str(state->param,
883 0 : smbXcli_conn_use_unicode(state->cli->conn),
884 0 : state->mask,
885 0 : strlen(state->mask)+1,
886 : NULL);
887 0 : if (tevent_req_nomem(state->param, req)) {
888 0 : return;
889 : }
890 : }
891 0 : param_len = talloc_get_size(state->param);
892 :
893 0 : if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
894 0 : additional_flags2 = FLAGS2_REPARSE_PATH;
895 : }
896 :
897 0 : subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
898 : SMBtrans2, NULL, -1, 0, 0,
899 0 : state->setup, 1, 0,
900 : state->param, param_len, 10,
901 : NULL, 0, CLI_BUFFER_SIZE);
902 0 : if (tevent_req_nomem(subreq, req)) {
903 0 : return;
904 : }
905 0 : tevent_req_set_callback(subreq, cli_list_trans_done, req);
906 : }
907 :
908 2673 : static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
909 : TALLOC_CTX *mem_ctx,
910 : struct file_info **finfo)
911 : {
912 2673 : struct cli_list_trans_state *state = tevent_req_data(
913 : req, struct cli_list_trans_state);
914 : NTSTATUS status;
915 :
916 2673 : if (tevent_req_is_nterror(req, &status)) {
917 81 : return status;
918 : }
919 2592 : *finfo = talloc_move(mem_ctx, &state->finfo);
920 2592 : return NT_STATUS_OK;
921 : }
922 :
923 0 : NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
924 : uint32_t attribute, int info_level,
925 : NTSTATUS (*fn)(
926 : struct file_info *finfo,
927 : const char *mask,
928 : void *private_data),
929 : void *private_data)
930 : {
931 0 : TALLOC_CTX *frame = talloc_stackframe();
932 : struct tevent_context *ev;
933 : struct tevent_req *req;
934 : int i, num_finfo;
935 0 : struct file_info *finfo = NULL;
936 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
937 :
938 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
939 : /*
940 : * Can't use sync call while an async call is in flight
941 : */
942 0 : status = NT_STATUS_INVALID_PARAMETER;
943 0 : goto fail;
944 : }
945 0 : ev = samba_tevent_context_init(frame);
946 0 : if (ev == NULL) {
947 0 : goto fail;
948 : }
949 0 : req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
950 0 : if (req == NULL) {
951 0 : goto fail;
952 : }
953 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
954 0 : goto fail;
955 : }
956 0 : status = cli_list_trans_recv(req, frame, &finfo);
957 0 : if (!NT_STATUS_IS_OK(status)) {
958 0 : goto fail;
959 : }
960 0 : num_finfo = talloc_array_length(finfo);
961 0 : for (i=0; i<num_finfo; i++) {
962 0 : status = fn(&finfo[i], mask, private_data);
963 0 : if (!NT_STATUS_IS_OK(status)) {
964 0 : goto fail;
965 : }
966 : }
967 0 : fail:
968 0 : TALLOC_FREE(frame);
969 0 : return status;
970 : }
971 :
972 : struct cli_list_state {
973 : struct tevent_context *ev;
974 : struct tevent_req *subreq;
975 : NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
976 : struct file_info **finfo);
977 : struct file_info *finfo;
978 : size_t num_received;
979 : };
980 :
981 : static void cli_list_done(struct tevent_req *subreq);
982 :
983 7201 : struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
984 : struct tevent_context *ev,
985 : struct cli_state *cli,
986 : const char *mask,
987 : uint32_t attribute,
988 : uint16_t info_level)
989 : {
990 7201 : struct tevent_req *req = NULL;
991 : struct cli_list_state *state;
992 7201 : enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
993 :
994 7201 : req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
995 7201 : if (req == NULL) {
996 0 : return NULL;
997 : }
998 7201 : state->ev = ev;
999 :
1000 7201 : if (proto >= PROTOCOL_SMB2_02) {
1001 5824 : state->subreq = cli_smb2_list_send(state, ev, cli, mask);
1002 5824 : state->recv_fn = cli_smb2_list_recv;
1003 1377 : } else if (proto >= PROTOCOL_LANMAN2) {
1004 1377 : state->subreq = cli_list_trans_send(
1005 : state, ev, cli, mask, attribute, info_level);
1006 1377 : state->recv_fn = cli_list_trans_recv;
1007 : } else {
1008 0 : state->subreq = cli_list_old_send(
1009 : state, ev, cli, mask, attribute);
1010 0 : state->recv_fn = cli_list_old_recv;
1011 : }
1012 7201 : if (tevent_req_nomem(state->subreq, req)) {
1013 0 : return tevent_req_post(req, ev);
1014 : }
1015 7201 : tevent_req_set_callback(state->subreq, cli_list_done, req);
1016 7201 : return req;
1017 : }
1018 :
1019 18572 : static void cli_list_done(struct tevent_req *subreq)
1020 : {
1021 18572 : struct tevent_req *req = tevent_req_callback_data(
1022 : subreq, struct tevent_req);
1023 18572 : struct cli_list_state *state = tevent_req_data(
1024 : req, struct cli_list_state);
1025 : NTSTATUS status;
1026 :
1027 18572 : SMB_ASSERT(subreq == state->subreq);
1028 :
1029 : /*
1030 : * We don't want to be called by the lowerlevel routines
1031 : * from within state->recv_fn()
1032 : */
1033 18572 : tevent_req_set_callback(subreq, NULL, NULL);
1034 :
1035 18572 : status = state->recv_fn(subreq, state, &state->finfo);
1036 18572 : if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1037 : /* We'll get back here */
1038 5764 : tevent_req_set_callback(subreq, cli_list_done, req);
1039 5764 : return;
1040 : }
1041 :
1042 12808 : if (tevent_req_nterror(req, status)) {
1043 5903 : return;
1044 : }
1045 6905 : tevent_req_notify_callback(req);
1046 : }
1047 :
1048 55201 : NTSTATUS cli_list_recv(
1049 : struct tevent_req *req,
1050 : TALLOC_CTX *mem_ctx,
1051 : struct file_info **pfinfo)
1052 : {
1053 55201 : struct cli_list_state *state = tevent_req_data(
1054 : req, struct cli_list_state);
1055 : size_t num_results;
1056 55201 : struct file_info *finfo = NULL;
1057 : NTSTATUS status;
1058 : bool in_progress;
1059 :
1060 55201 : in_progress = tevent_req_is_in_progress(req);
1061 :
1062 55201 : if (!in_progress) {
1063 7201 : if (!tevent_req_is_nterror(req, &status)) {
1064 0 : status = NT_STATUS_NO_MORE_FILES;
1065 : }
1066 7201 : return status;
1067 : }
1068 :
1069 48000 : if (state->finfo == NULL) {
1070 35294 : status = state->recv_fn(state->subreq, state, &state->finfo);
1071 :
1072 35294 : if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1073 5607 : tevent_req_set_callback(
1074 : state->subreq, cli_list_done, req);
1075 5607 : return NT_STATUS_RETRY;
1076 : }
1077 :
1078 29687 : if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1079 1296 : status = NT_STATUS_NO_MORE_FILES;
1080 : }
1081 :
1082 29687 : if (tevent_req_nterror(req, status)) {
1083 1298 : return status;
1084 : }
1085 :
1086 28389 : state->num_received = 0;
1087 : }
1088 :
1089 41095 : num_results = talloc_array_length(state->finfo);
1090 :
1091 41095 : if (num_results == 1) {
1092 34604 : finfo = talloc_move(mem_ctx, &state->finfo);
1093 : } else {
1094 6491 : struct file_info *src_finfo =
1095 6491 : &state->finfo[state->num_received];
1096 :
1097 6491 : finfo = talloc(mem_ctx, struct file_info);
1098 6491 : if (finfo == NULL) {
1099 0 : return NT_STATUS_NO_MEMORY;
1100 : }
1101 6491 : *finfo = *src_finfo;
1102 6491 : finfo->name = talloc_move(finfo, &src_finfo->name);
1103 6491 : finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1104 : }
1105 :
1106 41095 : state->num_received += 1;
1107 :
1108 41095 : if (state->num_received == num_results) {
1109 35294 : TALLOC_FREE(state->finfo);
1110 : }
1111 :
1112 41095 : tevent_req_defer_callback(req, state->ev);
1113 41095 : tevent_req_notify_callback(req);
1114 :
1115 41095 : *pfinfo = finfo;
1116 41095 : return NT_STATUS_OK;
1117 : }
1118 :
1119 : struct cli_list_sync_state {
1120 : const char *mask;
1121 : uint32_t attribute;
1122 : NTSTATUS (*fn)(struct file_info *finfo,
1123 : const char *mask,
1124 : void *private_data);
1125 : void *private_data;
1126 : NTSTATUS status;
1127 : bool processed_file;
1128 : };
1129 :
1130 42000 : static void cli_list_sync_cb(struct tevent_req *subreq)
1131 : {
1132 37137 : struct cli_list_sync_state *state =
1133 4863 : tevent_req_callback_data_void(subreq);
1134 : struct file_info *finfo;
1135 : bool ok;
1136 :
1137 42000 : state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1138 : /* No TALLOC_FREE(subreq), we get here more than once */
1139 :
1140 42000 : if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1141 : /*
1142 : * The lowlevel SMB call was rearmed, we'll get back
1143 : * here when it's done.
1144 : */
1145 3132 : state->status = NT_STATUS_OK;
1146 11736 : return;
1147 : }
1148 :
1149 38868 : if (!NT_STATUS_IS_OK(state->status)) {
1150 6024 : return;
1151 : }
1152 :
1153 32844 : ok = dir_check_ftype(finfo->attr, state->attribute);
1154 32844 : if (!ok) {
1155 : /*
1156 : * Only process if attributes match. On SMB1 server
1157 : * does this, so on SMB2 we need to emulate in the
1158 : * client.
1159 : *
1160 : * https://bugzilla.samba.org/show_bug.cgi?id=10260
1161 : */
1162 0 : return;
1163 : }
1164 :
1165 32844 : state->status = state->fn(finfo, state->mask, state->private_data);
1166 :
1167 32844 : state->processed_file = true;
1168 :
1169 32844 : TALLOC_FREE(finfo);
1170 : }
1171 :
1172 4726 : NTSTATUS cli_list(struct cli_state *cli,
1173 : const char *mask,
1174 : uint32_t attribute,
1175 : NTSTATUS (*fn)(struct file_info *finfo,
1176 : const char *mask,
1177 : void *private_data),
1178 : void *private_data)
1179 : {
1180 4726 : TALLOC_CTX *frame = NULL;
1181 4726 : struct cli_list_sync_state state = {
1182 : .mask = mask,
1183 : .attribute = attribute,
1184 : .fn = fn,
1185 : .private_data = private_data,
1186 : };
1187 : struct tevent_context *ev;
1188 : struct tevent_req *req;
1189 4726 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1190 : uint16_t info_level;
1191 :
1192 4726 : frame = talloc_stackframe();
1193 :
1194 4726 : if (smbXcli_conn_has_async_calls(cli->conn)) {
1195 : /*
1196 : * Can't use sync call while an async call is in flight
1197 : */
1198 0 : status = NT_STATUS_INVALID_PARAMETER;
1199 0 : goto fail;
1200 : }
1201 4726 : ev = samba_tevent_context_init(frame);
1202 4726 : if (ev == NULL) {
1203 0 : goto fail;
1204 : }
1205 :
1206 4726 : info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1207 : ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1208 :
1209 4726 : req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1210 4726 : if (req == NULL) {
1211 0 : goto fail;
1212 : }
1213 4726 : tevent_req_set_callback(req, cli_list_sync_cb, &state);
1214 :
1215 4726 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1216 0 : goto fail;
1217 : }
1218 :
1219 4726 : status = state.status;
1220 :
1221 4726 : if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1222 4583 : status = NT_STATUS_OK;
1223 : }
1224 :
1225 9028 : if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1226 155 : status = NT_STATUS_NO_SUCH_FILE;
1227 : }
1228 :
1229 9008 : fail:
1230 4726 : TALLOC_FREE(frame);
1231 4726 : return status;
1232 : }
|