Remove pstrings. Ensure we validate offsets.
[metze/samba/wip.git] / source3 / libsmb / clilist.c
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
22 extern file_info def_finfo;
23
24 /****************************************************************************
25  Calculate a safe next_entry_offset.
26 ****************************************************************************/
27
28 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
29 {
30         size_t next_entry_offset = (size_t)IVAL(base,0);
31
32         if (next_entry_offset == 0 ||
33                         base + next_entry_offset < base ||
34                         base + next_entry_offset > pdata_end) {
35                 next_entry_offset = pdata_end - base;
36         }
37         return next_entry_offset;
38 }
39
40 /****************************************************************************
41  Interpret a long filename structure - this is mostly guesses at the moment.
42  The length of the structure is returned
43  The structure of a long filename depends on the info level. 260 is used
44  by NT and 2 is used by OS/2
45 ****************************************************************************/
46
47 static size_t interpret_long_filename(struct cli_state *cli,
48                                         int level,
49                                         const char *p,
50                                         const char *pdata_end,
51                                         file_info *finfo,
52                                         uint32 *p_resume_key,
53                                         DATA_BLOB *p_last_name_raw)
54 {
55         file_info finfo2;
56         int len;
57         const char *base = p;
58
59         data_blob_free(p_last_name_raw);
60
61         if (!finfo) {
62                 finfo = &finfo2;
63         }
64
65         if (p_resume_key) {
66                 *p_resume_key = 0;
67         }
68         memcpy(finfo,&def_finfo,sizeof(*finfo));
69         finfo->cli = cli;
70
71         switch (level) {
72                 case 1: /* OS/2 understands this */
73                         /* these dates are converted to GMT by
74                            make_unix_date */
75                         if (pdata_end - base < 27) {
76                                 return pdata_end - base;
77                         }
78                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
79                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
80                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
81                         finfo->size = IVAL(p,16);
82                         finfo->mode = CVAL(p,24);
83                         len = CVAL(p, 26);
84                         p += 27;
85                         p += clistr_align_in(cli, p, 0);
86                         if (p + len + 2 > pdata_end) {
87                                 return pdata_end - base;
88                         }
89                         /* the len+2 below looks strange but it is
90                            important to cope with the differences
91                            between win2000 and win9x for this call
92                            (tridge) */
93                         p += clistr_pull(cli, finfo->name, p,
94                                          sizeof(finfo->name),
95                                          len+2,
96                                          STR_TERMINATE);
97                         return PTR_DIFF(p, base);
98
99                 case 2: /* this is what OS/2 uses mostly */
100                         /* these dates are converted to GMT by
101                            make_unix_date */
102                         if (pdata_end - base < 31) {
103                                 return pdata_end - base;
104                         }
105                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
106                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
107                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
108                         finfo->size = IVAL(p,16);
109                         finfo->mode = CVAL(p,24);
110                         len = CVAL(p, 30);
111                         p += 31;
112                         /* check for unisys! */
113                         if (p + len + 1 > pdata_end) {
114                                 return pdata_end - base;
115                         }
116                         p += clistr_pull(cli, finfo->name, p,
117                                          sizeof(finfo->name),
118                                          len,
119                                          STR_NOALIGN);
120                         return PTR_DIFF(p, base) + 1;
121
122                 case 260: /* NT uses this, but also accepts 2 */
123                 {
124                         size_t namelen, slen;
125
126                         if (pdata_end - base < 94) {
127                                 return pdata_end - base;
128                         }
129
130                         p += 4; /* next entry offset */
131
132                         if (p_resume_key) {
133                                 *p_resume_key = IVAL(p,0);
134                         }
135                         p += 4; /* fileindex */
136
137                         /* Offset zero is "create time", not "change time". */
138                         p += 8;
139                         finfo->atime_ts = interpret_long_date(p);
140                         p += 8;
141                         finfo->mtime_ts = interpret_long_date(p);
142                         p += 8;
143                         finfo->ctime_ts = interpret_long_date(p);
144                         p += 8;
145                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
146                         p += 8;
147                         p += 8; /* alloc size */
148                         finfo->mode = CVAL(p,0);
149                         p += 4;
150                         namelen = IVAL(p,0);
151                         p += 4;
152                         p += 4; /* EA size */
153                         slen = SVAL(p, 0);
154                         if (slen > 24) {
155                                 /* Bad short name length. */
156                                 return pdata_end - base;
157                         }
158                         p += 2;
159                         {
160                                 /* stupid NT bugs. grr */
161                                 int flags = 0;
162                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
163                                 clistr_pull(cli, finfo->short_name, p,
164                                             sizeof(finfo->short_name),
165                                             slen, flags);
166                         }
167                         p += 24; /* short name? */
168                         if (p + namelen < p || p + namelen > pdata_end) {
169                                 return pdata_end - base;
170                         }
171                         clistr_pull(cli, finfo->name, p,
172                                     sizeof(finfo->name),
173                                     namelen, 0);
174
175                         /* To be robust in the face of unicode conversion failures
176                            we need to copy the raw bytes of the last name seen here.
177                            Namelen doesn't include the terminating unicode null, so
178                            copy it here. */
179
180                         if (p_last_name_raw) {
181                                 *p_last_name_raw = data_blob(NULL, namelen+2);
182                                 memcpy(p_last_name_raw->data, p, namelen);
183                                 SSVAL(p_last_name_raw->data, namelen, 0);
184                         }
185                         return calc_next_entry_offset(base, pdata_end);
186                 }
187         }
188
189         DEBUG(1,("Unknown long filename format %d\n",level));
190         return calc_next_entry_offset(base, pdata_end);
191 }
192
193 /****************************************************************************
194  Do a directory listing, calling fn on each file found.
195 ****************************************************************************/
196
197 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
198                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
199 {
200 #if 1
201         int max_matches = 1366; /* Match W2k - was 512. */
202 #else
203         int max_matches = 512;
204 #endif
205         int info_level;
206         char *p, *p2, *rdata_end;
207         char *mask = NULL;
208         file_info finfo;
209         int i;
210         char *dirlist = NULL;
211         int dirlist_len = 0;
212         int total_received = -1;
213         bool First = True;
214         int ff_searchcount=0;
215         int ff_eos=0;
216         int ff_dir_handle=0;
217         int loop_count = 0;
218         char *rparam=NULL, *rdata=NULL;
219         unsigned int param_len, data_len;
220         uint16 setup;
221         char param[1024];
222         const char *mnt;
223         uint32 resume_key = 0;
224         DATA_BLOB last_name_raw = data_blob(NULL, 0);
225
226         /* NT uses 260, OS/2 uses 2. Both accept 1. */
227         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
228
229         mask = SMB_STRDUP(Mask);
230         if (!mask) {
231                 return -1;
232         }
233
234         while (ff_eos == 0) {
235                 loop_count++;
236                 if (loop_count > 200) {
237                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
238                         break;
239                 }
240
241                 if (First) {
242                         setup = TRANSACT2_FINDFIRST;
243                         SSVAL(param,0,attribute); /* attribute */
244                         SSVAL(param,2,max_matches); /* max count */
245                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
246                         SSVAL(param,6,info_level);
247                         SIVAL(param,8,0);
248                         p = param+12;
249                         p += clistr_push(cli, param+12, mask, sizeof(param)-12,
250                                          STR_TERMINATE);
251                 } else {
252                         setup = TRANSACT2_FINDNEXT;
253                         SSVAL(param,0,ff_dir_handle);
254                         SSVAL(param,2,max_matches); /* max count */
255                         SSVAL(param,4,info_level);
256                         /* For W2K servers serving out FAT filesystems we *must* set the
257                            resume key. If it's not FAT then it's returned as zero. */
258                         SIVAL(param,6,resume_key); /* ff_resume_key */
259                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
260                            can miss filenames. Use last filename continue instead. JRA */
261                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
262                         p = param+12;
263                         if (last_name_raw.length && (last_name_raw.length < (sizeof(param)-12))) {
264                                 memcpy(p, last_name_raw.data, last_name_raw.length);
265                                 p += last_name_raw.length;
266                         } else {
267                                 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
268                         }
269                 }
270
271                 param_len = PTR_DIFF(p, param);
272
273                 if (!cli_send_trans(cli, SMBtrans2,
274                                     NULL,                   /* Name */
275                                     -1, 0,                  /* fid, flags */
276                                     &setup, 1, 0,           /* setup, length, max */
277                                     param, param_len, 10,   /* param, length, max */
278                                     NULL, 0,
279 #if 0
280                                     /* w2k value. */
281                                     MIN(16384,cli->max_xmit) /* data, length, max. */
282 #else
283                                     cli->max_xmit           /* data, length, max. */
284 #endif
285                                     )) {
286                         break;
287                 }
288
289                 if (!cli_receive_trans(cli, SMBtrans2,
290                                        &rparam, &param_len,
291                                        &rdata, &data_len) &&
292                     cli_is_dos_error(cli)) {
293                         /* we need to work around a Win95 bug - sometimes
294                            it gives ERRSRV/ERRerror temprarily */
295                         uint8 eclass;
296                         uint32 ecode;
297
298                         SAFE_FREE(rdata);
299                         SAFE_FREE(rparam);
300
301                         cli_dos_error(cli, &eclass, &ecode);
302                         if (eclass != ERRSRV || ecode != ERRerror)
303                                 break;
304                         smb_msleep(100);
305                         continue;
306                 }
307
308                 if (cli_is_error(cli) || !rdata || !rparam) {
309                         SAFE_FREE(rdata);
310                         SAFE_FREE(rparam);
311                         break;
312                 }
313
314                 if (total_received == -1)
315                         total_received = 0;
316
317                 /* parse out some important return info */
318                 p = rparam;
319                 if (First) {
320                         ff_dir_handle = SVAL(p,0);
321                         ff_searchcount = SVAL(p,2);
322                         ff_eos = SVAL(p,4);
323                 } else {
324                         ff_searchcount = SVAL(p,0);
325                         ff_eos = SVAL(p,2);
326                 }
327
328                 if (ff_searchcount == 0) {
329                         SAFE_FREE(rdata);
330                         SAFE_FREE(rparam);
331                         break;
332                 }
333
334                 /* point to the data bytes */
335                 p = rdata;
336                 rdata_end = rdata + data_len;
337
338                 /* we might need the lastname for continuations */
339                 for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
340                         if ((info_level == 260) && (i == ff_searchcount-1)) {
341                                 /* Last entry - fixup the last offset length. */
342                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
343                         }
344                         p2 += interpret_long_filename(cli,
345                                                         info_level,
346                                                         p2,
347                                                         rdata_end,
348                                                         &finfo,
349                                                         &resume_key,
350                                                         &last_name_raw);
351
352                         if (!First && *mask && strcsequal(finfo.name, mask)) {
353                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
354                                         finfo.name));
355                                 ff_eos = 1;
356                                 break;
357                         }
358                 }
359
360                 SAFE_FREE(mask);
361                 if (ff_searchcount > 0) {
362                         mask = SMB_STRDUP(finfo.name);
363                 } else {
364                         mask = SMB_STRDUP("");
365                 }
366                 if (!mask) {
367                         SAFE_FREE(rdata);
368                         SAFE_FREE(rparam);
369                         break;
370                 }
371
372                 /* grab the data for later use */
373                 /* and add them to the dirlist pool */
374                 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
375
376                 if (!dirlist) {
377                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
378                         SAFE_FREE(rdata);
379                         SAFE_FREE(rparam);
380                         break;
381                 }
382
383                 memcpy(dirlist+dirlist_len,p,data_len);
384                 dirlist_len += data_len;
385
386                 total_received += ff_searchcount;
387
388                 SAFE_FREE(rdata);
389                 SAFE_FREE(rparam);
390
391                 DEBUG(3,("received %d entries (eos=%d)\n",
392                          ff_searchcount,ff_eos));
393
394                 if (ff_searchcount > 0)
395                         loop_count = 0;
396
397                 First = False;
398         }
399
400         mnt = cli_cm_get_mntpoint( cli );
401
402         /* see if the server disconnected or the connection otherwise failed */
403         if (cli_is_error(cli)) {
404                 total_received = -1;
405         } else {
406                 /* no connection problem.  let user function add each entry */
407                 rdata_end = dirlist + dirlist_len;
408                 for (p=dirlist,i=0;i<total_received;i++) {
409                         p += interpret_long_filename(cli,
410                                                         info_level,
411                                                         p,
412                                                         rdata_end,
413                                                         &finfo,
414                                                         NULL,
415                                                         NULL);
416                         fn( mnt,&finfo, Mask, state );
417                 }
418         }
419
420         /* free up the dirlist buffer and last name raw blob */
421         SAFE_FREE(dirlist);
422         data_blob_free(&last_name_raw);
423         SAFE_FREE(mask);
424         return(total_received);
425 }
426
427 /****************************************************************************
428  Interpret a short filename structure.
429  The length of the structure is returned.
430 ****************************************************************************/
431
432 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
433 {
434
435         *finfo = def_finfo;
436
437         finfo->cli = cli;
438         finfo->mode = CVAL(p,21);
439
440         /* this date is converted to GMT by make_unix_date */
441         finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
442         finfo->ctime_ts.tv_nsec = 0;
443         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
444         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
445         finfo->size = IVAL(p,26);
446         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
447         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
448                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
449                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
450         }
451
452         return(DIR_STRUCT_SIZE);
453 }
454
455
456 /****************************************************************************
457  Do a directory listing, calling fn on each file found.
458  this uses the old SMBsearch interface. It is needed for testing Samba,
459  but should otherwise not be used.
460 ****************************************************************************/
461
462 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
463                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
464 {
465         char *p;
466         int received = 0;
467         bool first = True;
468         char status[21];
469         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
470         int num_received = 0;
471         int i;
472         char *dirlist = NULL;
473         char *mask = NULL;
474
475         ZERO_ARRAY(status);
476
477         mask = SMB_STRDUP(Mask);
478         if (!mask) {
479                 return -1;
480         }
481
482         while (1) {
483                 memset(cli->outbuf,'\0',smb_size);
484                 memset(cli->inbuf,'\0',smb_size);
485
486                 set_message(cli->outbuf,2,0,True);
487
488                 SCVAL(cli->outbuf,smb_com,SMBsearch);
489
490                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
491                 cli_setup_packet(cli);
492
493                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
494                 SSVAL(cli->outbuf,smb_vwv1,attribute);
495
496                 p = smb_buf(cli->outbuf);
497                 *p++ = 4;
498
499                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
500                 *p++ = 5;
501                 if (first) {
502                         SSVAL(p,0,0);
503                         p += 2;
504                 } else {
505                         SSVAL(p,0,21);
506                         p += 2;
507                         memcpy(p,status,21);
508                         p += 21;
509                 }
510
511                 cli_setup_bcc(cli, p);
512                 cli_send_smb(cli);
513                 if (!cli_receive_smb(cli)) break;
514
515                 received = SVAL(cli->inbuf,smb_vwv0);
516                 if (received <= 0) break;
517
518                 first = False;
519
520                 dirlist = (char *)SMB_REALLOC(
521                         dirlist,(num_received + received)*DIR_STRUCT_SIZE);
522                 if (!dirlist) {
523                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
524                         SAFE_FREE(mask);
525                         return 0;
526                 }
527
528                 p = smb_buf(cli->inbuf) + 3;
529
530                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
531                        p,received*DIR_STRUCT_SIZE);
532
533                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
534
535                 num_received += received;
536
537                 if (cli_is_error(cli)) break;
538         }
539
540         if (!first) {
541                 memset(cli->outbuf,'\0',smb_size);
542                 memset(cli->inbuf,'\0',smb_size);
543
544                 set_message(cli->outbuf,2,0,True);
545                 SCVAL(cli->outbuf,smb_com,SMBfclose);
546                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
547                 cli_setup_packet(cli);
548
549                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
550                 SSVAL(cli->outbuf, smb_vwv1, attribute);
551
552                 p = smb_buf(cli->outbuf);
553                 *p++ = 4;
554                 fstrcpy(p, "");
555                 p += strlen(p) + 1;
556                 *p++ = 5;
557                 SSVAL(p, 0, 21);
558                 p += 2;
559                 memcpy(p,status,21);
560                 p += 21;
561
562                 cli_setup_bcc(cli, p);
563                 cli_send_smb(cli);
564                 if (!cli_receive_smb(cli)) {
565                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
566                 }
567         }
568
569         for (p=dirlist,i=0;i<num_received;i++) {
570                 file_info finfo;
571                 p += interpret_short_filename(cli, p,&finfo);
572                 fn("\\", &finfo, Mask, state);
573         }
574
575         SAFE_FREE(mask);
576         SAFE_FREE(dirlist);
577         return(num_received);
578 }
579
580 /****************************************************************************
581  Do a directory listing, calling fn on each file found.
582  This auto-switches between old and new style.
583 ****************************************************************************/
584
585 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
586              void (*fn)(const char *, file_info *, const char *, void *), void *state)
587 {
588         if (cli->protocol <= PROTOCOL_LANMAN1)
589                 return cli_list_old(cli, Mask, attribute, fn, state);
590         return cli_list_new(cli, Mask, attribute, fn, state);
591 }