bounds check next_token() to prevent possible buffer overflows
[gd/samba-autobuild/.git] / source3 / client / smbmount.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    SMB client
5    Copyright (C) Andrew Tridgell 1994-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #define NO_SYSLOG
23
24 #include <linux/version.h>
25 #define LVERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
26 #if LINUX_VERSION_CODE < LVERSION(2,1,70)
27 #error this code will only compile on versions of linux after 2.1.70
28 #endif
29
30 #include "includes.h"
31 #include <linux/smb_fs.h>
32 static struct smb_conn_opt conn_options;
33
34 #ifndef REGISTER
35 #define REGISTER 0
36 #endif
37
38 /* Uncomment this to allow debug the smbmount daemon */
39 #define SMBFS_DEBUG 1
40
41 pstring cur_dir = "\\";
42 pstring cd_path = "";
43 extern pstring service;
44 extern pstring desthost;
45 extern pstring global_myname;
46 extern pstring myhostname;
47 extern pstring password;
48 extern pstring username;
49 extern pstring workgroup;
50 char *cmdstr="";
51 extern BOOL got_pass;
52 extern BOOL connect_as_printer;
53 extern BOOL connect_as_ipc;
54 extern struct in_addr ipzero;
55
56 extern BOOL doencrypt;
57
58 extern pstring user_socket_options;
59
60 /* 30 second timeout on most commands */
61 #define CLIENT_TIMEOUT (30*1000)
62 #define SHORT_TIMEOUT (5*1000)
63
64 /* value for unused fid field in trans2 secondary request */
65 #define FID_UNUSED (0xFFFF)
66
67 extern int name_type;
68
69 extern int max_protocol;
70 int port = SMB_PORT;
71
72
73 time_t newer_than = 0;
74 int archive_level = 0;
75
76 extern pstring debugf;
77 extern int DEBUGLEVEL;
78
79 BOOL translation = False;
80
81 extern int cnum;
82 extern int mid;
83 extern int pid;
84 extern int tid;
85 extern int gid;
86 extern int uid;
87
88 extern BOOL have_ip;
89 extern int max_xmit;
90
91 /* clitar bits insert */
92 extern int blocksize;
93 extern BOOL tar_inc;
94 extern BOOL tar_reset;
95 /* clitar bits end */
96  
97
98 int myumask = 0755;
99
100 extern pstring scope;
101
102 BOOL prompt = True;
103
104 int printmode = 1;
105
106 BOOL recurse = False;
107 BOOL lowercase = False;
108
109 struct in_addr dest_ip;
110
111 #define SEPARATORS " \t\n\r"
112
113 BOOL abort_mget = True;
114
115 extern int Protocol;
116
117 extern BOOL readbraw_supported ;
118 extern BOOL writebraw_supported;
119
120 pstring fileselection = "";
121
122 extern file_info def_finfo;
123
124 /* timing globals */
125 int get_total_size = 0;
126 int get_total_time_ms = 0;
127 int put_total_size = 0;
128 int put_total_time_ms = 0;
129
130 /* totals globals */
131 int dir_total = 0;
132
133 extern int Client;
134
135 #define USENMB
136
137 #define CNV_LANG(s) dos_to_unix(s,False)
138 #define CNV_INPUT(s) unix_to_dos(s,True)
139
140 /****************************************************************************
141 check for existance of a dir
142 ****************************************************************************/
143 static BOOL chkpath(char *path,BOOL report)
144 {
145   fstring path2;
146   pstring inbuf,outbuf;
147   char *p;
148
149   fstrcpy(path2,path);
150   trim_string(path2,NULL,"\\");
151   if (!*path2) *path2 = '\\';
152
153   bzero(outbuf,smb_size);
154   set_message(outbuf,0,4 + strlen(path2),True);
155   SCVAL(outbuf,smb_com,SMBchkpth);
156   SSVAL(outbuf,smb_tid,cnum);
157   cli_setup_pkt(outbuf);
158
159   p = smb_buf(outbuf);
160   *p++ = 4;
161   fstrcpy(p,path2);
162
163 #if 0
164   {
165           /* this little bit of code can be used to extract NT error codes.
166              Just feed a bunch of "cd foo" commands to smbclient then watch
167              in netmon (tridge) */
168           static int code=0;
169           SIVAL(outbuf, smb_rcls, code | 0xC0000000);
170           SSVAL(outbuf, smb_flg2, SVAL(outbuf, smb_flg2) | (1<<14));
171           code++;
172   }
173 #endif
174
175   send_smb(Client,outbuf);
176   client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
177
178   if (report && CVAL(inbuf,smb_rcls) != 0)
179     DEBUG(2,("chkpath: %s\n",smb_errstr(inbuf)));
180
181   return(CVAL(inbuf,smb_rcls) == 0);
182 }
183
184 static void
185 daemonize(void)
186 {
187         int i;
188         if ((i = fork()) < 0)
189         {
190                 DEBUG(0, ("could not fork\n"));
191         }
192         if (i > 0)
193         {
194                 /* parent simply exits */
195                 exit(0);
196         }
197         setsid();
198         chdir("/");
199 }
200
201 static void
202 close_our_files(void)
203 {
204         int i;
205         for (i = 0; i < NR_OPEN; i++) {
206                 if (i == Client) {
207                         continue;
208                 }
209                 close(i);
210         }
211 }
212
213 static void
214 usr1_handler(int x)
215 {
216         return;
217 }
218
219 /*
220  * Send a login and store the connection options. This is a separate
221  * function to keep clientutil.c independent of linux kernel changes.
222  */
223 static BOOL mount_send_login(char *inbuf, char *outbuf)
224 {
225   struct connection_options opt;
226   int res = cli_send_login(inbuf, outbuf, True, True, &opt);
227
228   if (!res)
229     return res;
230
231   conn_options.protocol = opt.protocol;
232   conn_options.case_handling = CASE_LOWER;
233   conn_options.max_xmit = opt.max_xmit;
234   conn_options.server_uid = opt.server_uid;
235   conn_options.tid = opt.tid;
236   conn_options.secmode = opt.sec_mode;
237   conn_options.maxmux = opt.max_mux;
238   conn_options.maxvcs = opt.max_vcs;
239   conn_options.rawmode = opt.rawmode;
240   conn_options.sesskey = opt.sesskey;
241   conn_options.maxraw = opt.maxraw;
242   conn_options.capabilities = opt.capabilities;
243   conn_options.serverzone = opt.serverzone;
244
245   return True;
246 }
247
248 /*
249  * Call the smbfs ioctl to install a connection socket,
250  * then wait for a signal to reconnect. Note that we do
251  * not exit after open_sockets() or send_login() errors,
252  * as the smbfs mount would then have no way to recover.
253  */
254 static void
255 send_fs_socket(char *mount_point, char *inbuf, char *outbuf)
256 {
257         int fd, closed = 0, res = 1;
258
259         while (1)
260         {
261                 if ((fd = open(mount_point, O_RDONLY)) < 0)
262                 {
263                         DEBUG(0, ("smbmount: can't open %s\n", mount_point));
264                         break;
265                 }               
266
267                 /*
268                  * Call the ioctl even if we couldn't get a socket ...
269                  * there's no point in making smbfs wait for a timeout.
270                  */
271                 conn_options.fd = -1;
272                 if (res)
273                         conn_options.fd = Client;
274                 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
275                 if (res != 0)
276                 {
277                         DEBUG(0, ("smbmount: ioctl failed, res=%d\n", res));
278                 }
279
280                 close_sockets();
281                 close(fd);
282                 /*
283                  * Close all open files if we haven't done so yet.
284                  */
285 #ifndef SMBFS_DEBUG
286                 if (!closed)
287                 {
288                         closed = 1;
289                         close_our_files();
290                 }
291 #endif
292
293                 /*
294                  * Wait for a signal from smbfs ...
295                  */
296                 CatchSignal(SIGUSR1, &usr1_handler);
297                 pause();
298                 DEBUG(0, ("smbmount: got signal, getting new socket\n"));
299
300                 res = cli_open_sockets(port);
301                 if (!res)
302                 {
303                         DEBUG(0, ("smbmount: can't open sockets\n"));
304                         continue;
305                 }
306
307                 res = mount_send_login(inbuf, outbuf);
308                 if (!res)
309                 {
310                         DEBUG(0, ("smbmount: login failed\n"));
311                 }
312         }
313         DEBUG(0, ("smbmount: exit\n"));
314         exit(1);
315 }
316
317 /****************************************************************************
318 mount smbfs
319 ****************************************************************************/
320 static void cmd_mount(char *inbuf,char *outbuf)
321 {
322         pstring mpoint;
323         pstring share_name;
324         pstring mount_command;
325         fstring buf;
326         int retval;
327         char mount_point[MAXPATHLEN+1];
328
329         if (!next_token(NULL, mpoint, NULL, sizeof(mpoint)))
330         {
331                 DEBUG(0,("You must supply a mount point\n"));
332                 return;
333         }
334
335         memset(mount_point, 0, sizeof(mount_point));
336
337         if (realpath(mpoint, mount_point) == NULL)
338         {
339                 DEBUG(0, ("Could not resolve mount point\n"));
340                 return;
341         }
342
343         /*
344          * Build the service name to report on the Unix side,
345          * converting '\' to '/' and ' ' to '_'.
346          */
347         pstrcpy(share_name, service);  
348         string_replace(share_name, '\\', '/');
349         string_replace(share_name, ' ', '_');
350
351         slprintf(mount_command, sizeof(mount_command)-1,"smbmnt %s -s %s", mount_point, share_name);
352
353         while(next_token(NULL, buf, NULL, sizeof(buf)))
354         {
355                 pstrcat(mount_command, " ");
356                 pstrcat(mount_command, buf);
357         }
358
359         DEBUG(3, ("mount command: %s\n", mount_command));
360
361         /*
362          * Create the background process before trying the mount.
363          * (We delay closing files to allow diagnostic messages.)
364          */
365         daemonize();
366
367         /* The parent has exited here, the child handles the connection: */
368         if ((retval = system(mount_command)) != 0)
369         {
370                 DEBUG(0,("mount failed\n"));
371                 exit(1);
372         }
373         send_fs_socket(mount_point, inbuf, outbuf);
374 }       
375
376
377 /* This defines the commands supported by this client */
378 struct
379 {
380   char *name;
381   void (*fn)();
382   char *description;
383 } commands[] = 
384 {
385   {"mount", cmd_mount, "<mount-point options> mount an smbfs file system"},
386   {"",NULL,NULL}
387 };
388
389
390 /*******************************************************************
391   lookup a command string in the list of commands, including 
392   abbreviations
393   ******************************************************************/
394 static int process_tok(fstring tok)
395 {
396   int i = 0, matches = 0;
397   int cmd=0;
398   int tok_len = strlen(tok);
399   
400   while (commands[i].fn != NULL)
401     {
402       if (strequal(commands[i].name,tok))
403         {
404           matches = 1;
405           cmd = i;
406           break;
407         }
408       else if (strnequal(commands[i].name, tok, tok_len))
409         {
410           matches++;
411           cmd = i;
412         }
413       i++;
414     }
415   
416   if (matches == 0)
417     return(-1);
418   else if (matches == 1)
419     return(cmd);
420   else
421     return(-2);
422 }
423
424 /****************************************************************************
425 help
426 ****************************************************************************/
427 void cmd_help(char *dum_in, char *dum_out)
428 {
429   int i=0,j;
430   fstring buf;
431
432   if (next_token(NULL,buf,NULL,sizeof(buf)))
433     {
434       if ((i = process_tok(buf)) >= 0)
435         DEBUG(0,("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description));                   
436     }
437   else
438     while (commands[i].description)
439       {
440         for (j=0; commands[i].description && (j<5); j++) {
441           DEBUG(0,("%-15s",commands[i].name));
442           i++;
443         }
444         DEBUG(0,("\n"));
445       }
446 }
447
448 /****************************************************************************
449 wait for keyboard activity, swallowing network packets
450 ****************************************************************************/
451 static void wait_keyboard(char *buffer)
452 {
453   fd_set fds;
454   int selrtn;
455   struct timeval timeout;
456   
457   while (1) 
458     {
459       extern int Client;
460       FD_ZERO(&fds);
461       FD_SET(Client,&fds);
462       FD_SET(fileno(stdin),&fds);
463
464       timeout.tv_sec = 20;
465       timeout.tv_usec = 0;
466       selrtn = sys_select(MAX(Client,fileno(stdin))+1,&fds,&timeout);
467       
468       if (FD_ISSET(fileno(stdin),&fds))
469         return;
470
471       /* We deliberately use receive_smb instead of
472          client_receive_smb as we want to receive
473          session keepalives and then drop them here.
474        */
475       if (FD_ISSET(Client,&fds))
476         receive_smb(Client,buffer,0);
477       
478       chkpath("\\",False);
479     }  
480 }
481
482
483 /****************************************************************************
484   process commands from the client
485 ****************************************************************************/
486 static BOOL process(char *base_directory)
487 {
488   extern FILE *dbf;
489   pstring line;
490   char *cmd;
491
492   char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
493   char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
494
495   if ((InBuffer == NULL) || (OutBuffer == NULL)) 
496     return(False);
497   
498   bzero(OutBuffer,smb_size);
499
500   if (!mount_send_login(InBuffer,OutBuffer))
501     return(False);
502
503   cmd = cmdstr;
504   if (cmd[0] != '\0') while (cmd[0] != '\0')
505     {
506       char *p;
507       fstring tok;
508       int i;
509
510       if ((p = strchr(cmd, ';')) == 0)
511         {
512           strncpy(line, cmd, 999);
513           line[1000] = '\0';
514           cmd += strlen(cmd);
515         }
516       else
517         {
518           if (p - cmd > 999) p = cmd + 999;
519           strncpy(line, cmd, p - cmd);
520           line[p - cmd] = '\0';
521           cmd = p + 1;
522         }
523
524       /* input language code to internal one */
525       CNV_INPUT (line);
526       
527       /* and get the first part of the command */
528       {
529         char *ptr = line;
530         if (!next_token(&ptr,tok,NULL,sizeof(tok))) continue;
531       }
532
533       if ((i = process_tok(tok)) >= 0)
534         commands[i].fn(InBuffer,OutBuffer);
535       else if (i == -2)
536         DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
537       else
538         DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
539     }
540   else while (!feof(stdin))
541     {
542       fstring tok;
543       int i;
544
545       bzero(OutBuffer,smb_size);
546
547       /* display a prompt */
548       DEBUG(0,("smb: %s> ", CNV_LANG(cur_dir)));
549       dbgflush();
550
551       wait_keyboard(InBuffer);
552   
553       /* and get a response */
554       if (!fgets(line,1000,stdin))
555         break;
556
557       /* input language code to internal one */
558       CNV_INPUT (line);
559
560       /* special case - first char is ! */
561       if (*line == '!')
562         {
563           system(line + 1);
564           continue;
565         }
566       
567       /* and get the first part of the command */
568       {
569         char *ptr = line;
570         if (!next_token(&ptr,tok,NULL,sizeof(tok))) continue;
571       }
572
573       if ((i = process_tok(tok)) >= 0)
574         commands[i].fn(InBuffer,OutBuffer);
575       else if (i == -2)
576         DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
577       else
578         DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
579     }
580   
581   cli_send_logout(InBuffer,OutBuffer);
582   return(True);
583 }
584
585 /****************************************************************************
586 usage on the program
587 ****************************************************************************/
588 static void usage(char *pname)
589 {
590   DEBUG(0,("Usage: %s service <password> [-p port] [-d debuglevel] [-l log] ",
591            pname));
592
593   DEBUG(0,("\nVersion %s\n",VERSION));
594   DEBUG(0,("\t-p port               connect to the specified port\n"));
595   DEBUG(0,("\t-d debuglevel         set the debuglevel\n"));
596   DEBUG(0,("\t-l log basename.      Basename for log/debug files\n"));
597   DEBUG(0,("\t-n netbios name.      Use this name as my netbios name\n"));
598   DEBUG(0,("\t-N                    don't ask for a password\n"));
599   DEBUG(0,("\t-m max protocol       set the max protocol level\n"));
600   DEBUG(0,("\t-I dest IP            use this IP to connect to\n"));
601   DEBUG(0,("\t-E                    write messages to stderr instead of stdout\n"));
602   DEBUG(0,("\t-U username           set the network username\n"));
603   DEBUG(0,("\t-W workgroup          set the workgroup name\n"));
604   DEBUG(0,("\t-c command string     execute semicolon separated commands\n"));
605   DEBUG(0,("\t-t terminal code      terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n"));
606   DEBUG(0,("\t-D directory          start from directory\n"));
607   DEBUG(0,("\n"));
608 }
609
610 /****************************************************************************
611   main program
612 ****************************************************************************/
613  int main(int argc,char *argv[])
614 {
615   fstring base_directory;
616   char *pname = argv[0];
617   int opt;
618   extern FILE *dbf;
619   extern char *optarg;
620   extern int optind;
621   pstring query_host;
622   BOOL nt_domain_logon = False;
623   static pstring servicesf = CONFIGFILE;
624   pstring term_code;
625   char *p;
626
627 #ifdef KANJI
628   pstrcpy(term_code, KANJI);
629 #else /* KANJI */
630   *term_code = 0;
631 #endif /* KANJI */
632
633   *query_host = 0;
634   *base_directory = 0;
635
636   DEBUGLEVEL = 2;
637
638   setup_logging(pname,True);
639
640   TimeInit();
641   charset_initialise();
642
643   pid = getpid();
644   uid = getuid();
645   gid = getgid();
646   mid = pid + 100;
647   myumask = umask(0);
648   umask(myumask);
649
650   if (getenv("USER"))
651   {
652     pstrcpy(username,getenv("USER"));
653
654     /* modification to support userid%passwd syntax in the USER var
655        25.Aug.97, jdblair@uab.edu */
656
657     if ((p=strchr(username,'%')))
658     {
659       *p = 0;
660       pstrcpy(password,p+1);
661       got_pass = True;
662       memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
663     }
664     strupper(username);
665   }
666
667  /* modification to support PASSWD environmental var
668   25.Aug.97, jdblair@uab.edu */
669
670   if (getenv("PASSWD"))
671     pstrcpy(password,getenv("PASSWD"));
672
673   if (*username == 0 && getenv("LOGNAME"))
674     {
675       pstrcpy(username,getenv("LOGNAME"));
676       strupper(username);
677     }
678
679   if (argc < 2)
680     {
681       usage(pname);
682       exit(1);
683     }
684   
685   if (*argv[1] != '-')
686     {
687
688       pstrcpy(service, argv[1]);  
689       /* Convert any '/' characters in the service name to '\' characters */
690       string_replace( service, '/','\\');
691       argc--;
692       argv++;
693
694       if (count_chars(service,'\\') < 3)
695         {
696           usage(pname);
697           printf("\n%s: Not enough '\\' characters in service\n",service);
698           exit(1);
699         }
700
701       if (argc > 1 && (*argv[1] != '-'))
702         {
703           got_pass = True;
704           pstrcpy(password,argv[1]);  
705           memset(argv[1],'X',strlen(argv[1]));
706           argc--;
707           argv++;
708         }
709     }
710
711   while ((opt = 
712           getopt(argc, argv,"s:B:O:M:S:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
713     switch (opt)
714       {
715       case 'm':
716         max_protocol = interpret_protocol(optarg,max_protocol);
717         break;
718       case 'O':
719         pstrcpy(user_socket_options,optarg);
720         break;  
721       case 'S':
722         pstrcpy(desthost,optarg);
723         strupper(desthost);
724         nt_domain_logon = True;
725         break;
726       case 'B':
727         iface_set_default(NULL,optarg,NULL);
728         break;
729       case 'D':
730         pstrcpy(base_directory,optarg);
731         break;
732       case 'i':
733         pstrcpy(scope,optarg);
734         break;
735       case 'U':
736         {
737           char *lp;
738         pstrcpy(username,optarg);
739         if ((lp=strchr(username,'%')))
740           {
741             *lp = 0;
742             pstrcpy(password,lp+1);
743             got_pass = True;
744             memset(strchr(optarg,'%')+1,'X',strlen(password));
745           }
746         }
747             
748         break;
749       case 'W':
750         pstrcpy(workgroup,optarg);
751         break;
752       case 'E':
753         dbf = stderr;
754         break;
755       case 'I':
756         {
757           dest_ip = *interpret_addr2(optarg);
758           if (zero_ip(dest_ip)) exit(1);
759           have_ip = True;
760         }
761         break;
762       case 'n':
763         pstrcpy(global_myname,optarg);
764         break;
765       case 'N':
766         got_pass = True;
767         break;
768       case 'd':
769         if (*optarg == 'A')
770           DEBUGLEVEL = 10000;
771         else
772           DEBUGLEVEL = atoi(optarg);
773         break;
774       case 'l':
775         slprintf(debugf,sizeof(debugf)-1,"%s.client",optarg);
776         break;
777       case 'p':
778         port = atoi(optarg);
779         break;
780       case 'c':
781         cmdstr = optarg;
782         got_pass = True;
783         break;
784       case 'h':
785         usage(pname);
786         exit(0);
787         break;
788       case 's':
789         pstrcpy(servicesf, optarg);
790         break;
791       case 't':
792         pstrcpy(term_code, optarg);
793         break;
794       default:
795         usage(pname);
796         exit(1);
797       }
798
799   if (!*query_host && !*service)
800     {
801       usage(pname);
802       exit(1);
803     }
804
805
806   DEBUG( 3, ( "Client started (version %s)\n", VERSION ) );
807
808   if(!get_myname(myhostname,NULL))
809   {
810     DEBUG(0,("Failed to get my hostname.\n"));
811   }
812
813   if (!lp_load(servicesf,True,False,False)) {
814     fprintf(stderr, "Can't load %s - run testparm to debug it\n", servicesf);
815   }
816
817   codepage_initialise(lp_client_code_page());
818
819   interpret_coding_system(term_code);
820
821   if (*workgroup == 0)
822     pstrcpy(workgroup,lp_workgroup());
823
824   load_interfaces();
825   get_myname((*global_myname)?NULL:global_myname,NULL);  
826   strupper(global_myname);
827
828   if (cli_open_sockets(port))
829     {
830       if (!process(base_directory))
831         {
832           close_sockets();
833           return(1);
834         }
835       close_sockets();
836     }
837   else
838     return(1);
839
840   return(0);
841 }