Fix the %m security bug again - and try to make it harder to reintroduce in
[tprouty/samba.git] / source / client / smbmount.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMBFS mount program
4    Copyright (C) Andrew Tridgell 1999
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #define NO_SYSLOG
22
23 #include "includes.h"
24
25 #include <mntent.h>
26 #include <asm/types.h>
27 #include <linux/smb_fs.h>
28
29 extern BOOL in_client;
30 extern pstring user_socket_options;
31 extern BOOL append_log;
32
33 static pstring credentials;
34 static pstring my_netbios_name;
35 static pstring password;
36 static pstring username;
37 static pstring workgroup;
38 static pstring mpoint;
39 static pstring service;
40 static pstring options;
41
42 static struct in_addr dest_ip;
43 static BOOL have_ip;
44 static int smb_port = 0;
45 static BOOL got_pass;
46 static uid_t mount_uid;
47 static gid_t mount_gid;
48 static int mount_ro;
49 static unsigned mount_fmask;
50 static unsigned mount_dmask;
51
52 static void usage(void);
53
54 static void exit_parent(int sig)
55 {
56         /* parent simply exits when child says go... */
57         exit(0);
58 }
59
60 static void daemonize(void)
61 {
62         int j, status;
63         pid_t child_pid;
64
65         signal( SIGTERM, exit_parent );
66
67         if ((child_pid = sys_fork()) < 0) {
68                 DEBUG(0,("could not fork\n"));
69         }
70
71         if (child_pid > 0) {
72                 while( 1 ) {
73                         j = waitpid( child_pid, &status, 0 );
74                         if( j < 0 ) {
75                                 if( EINTR == errno ) {
76                                         continue;
77                                 }
78                                 status = errno;
79                         }
80                         break;
81                 }
82                 /* If we get here - the child exited with some error status */
83                 exit(status);
84         }
85
86         signal( SIGTERM, SIG_DFL );
87         chdir("/");
88 }
89
90 static void close_our_files(int client_fd)
91 {
92         int i;
93         struct rlimit limits;
94
95         getrlimit(RLIMIT_NOFILE,&limits);
96         for (i = 0; i< limits.rlim_max; i++) {
97                 if (i == client_fd)
98                         continue;
99                 close(i);
100         }
101 }
102
103 static void usr1_handler(int x)
104 {
105         return;
106 }
107
108
109 /***************************************************** 
110 return a connection to a server
111 *******************************************************/
112 static struct cli_state *do_connection(char *the_service)
113 {
114         struct cli_state *c;
115         struct nmb_name called, calling;
116         char *server_n;
117         struct in_addr ip;
118         pstring server;
119         char *share;
120
121         if (the_service[0] != '\\' || the_service[1] != '\\') {
122                 usage();
123                 exit(1);
124         }
125
126         pstrcpy(server, the_service+2);
127         share = strchr_m(server,'\\');
128         if (!share) {
129                 usage();
130                 exit(1);
131         }
132         *share = 0;
133         share++;
134
135         server_n = server;
136
137         make_nmb_name(&calling, my_netbios_name, 0x0);
138         make_nmb_name(&called , server, 0x20);
139
140  again:
141         zero_ip(&ip);
142         if (have_ip) ip = dest_ip;
143
144         /* have to open a new connection */
145         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
146             !cli_connect(c, server_n, &ip)) {
147                 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
148                 if (c) {
149                         cli_shutdown(c);
150                 }
151                 return NULL;
152         }
153
154         /* SPNEGO doesn't work till we get NTSTATUS error support */
155         c->use_spnego = False;
156
157         if (!cli_session_request(c, &calling, &called)) {
158                 char *p;
159                 DEBUG(0,("%d: session request to %s failed (%s)\n", 
160                          sys_getpid(), called.name, cli_errstr(c)));
161                 cli_shutdown(c);
162                 if ((p=strchr_m(called.name, '.'))) {
163                         *p = 0;
164                         goto again;
165                 }
166                 if (strcmp(called.name, "*SMBSERVER")) {
167                         make_nmb_name(&called , "*SMBSERVER", 0x20);
168                         goto again;
169                 }
170                 return NULL;
171         }
172
173         DEBUG(4,("%d: session request ok\n", sys_getpid()));
174
175         if (!cli_negprot(c)) {
176                 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
177                 cli_shutdown(c);
178                 return NULL;
179         }
180
181         if (!got_pass) {
182                 char *pass = getpass("Password: ");
183                 if (pass) {
184                         pstrcpy(password, pass);
185                 }
186         }
187
188         /* This should be right for current smbfs. Future versions will support
189           large files as well as unicode and oplocks. */
190         c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
191                                 CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
192         c->force_dos_errors = True;
193         if (!cli_session_setup(c, username, 
194                                password, strlen(password),
195                                password, strlen(password),
196                                workgroup)) {
197                 /* if a password was not supplied then try again with a
198                         null username */
199                 if (password[0] || !username[0] ||
200                                 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
201                         DEBUG(0,("%d: session setup failed: %s\n",
202                                 sys_getpid(), cli_errstr(c)));
203                         cli_shutdown(c);
204                         return NULL;
205                 }
206                 DEBUG(0,("Anonymous login successful\n"));
207         }
208
209         DEBUG(4,("%d: session setup ok\n", sys_getpid()));
210
211         if (!cli_send_tconX(c, share, "?????",
212                             password, strlen(password)+1)) {
213                 DEBUG(0,("%d: tree connect failed: %s\n",
214                          sys_getpid(), cli_errstr(c)));
215                 cli_shutdown(c);
216                 return NULL;
217         }
218
219         DEBUG(4,("%d: tconx ok\n", sys_getpid()));
220
221         got_pass = True;
222
223         return c;
224 }
225
226
227 /****************************************************************************
228 unmount smbfs  (this is a bailout routine to clean up if a reconnect fails)
229         Code blatently stolen from smbumount.c
230                 -mhw-
231 ****************************************************************************/
232 static void smb_umount(char *mount_point)
233 {
234         int fd;
235         struct mntent *mnt;
236         FILE* mtab;
237         FILE* new_mtab;
238
239         /* Programmers Note:
240                 This routine only gets called to the scene of a disaster
241                 to shoot the survivors...  A connection that was working
242                 has now apparently failed.  We have an active mount point
243                 (presumably) that we need to dump.  If we get errors along
244                 the way - make some noise, but we are already turning out
245                 the lights to exit anyways...
246         */
247         if (umount(mount_point) != 0) {
248                 DEBUG(0,("%d: Could not umount %s: %s\n",
249                          sys_getpid(), mount_point, strerror(errno)));
250                 return;
251         }
252
253         if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
254                 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
255                 return;
256         }
257
258         close(fd);
259         
260         if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
261                 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
262                          sys_getpid(), strerror(errno)));
263                 return;
264         }
265
266 #define MOUNTED_TMP MOUNTED".tmp"
267
268         if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
269                 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
270                          sys_getpid(), strerror(errno)));
271                 endmntent(mtab);
272                 return;
273         }
274
275         while ((mnt = getmntent(mtab)) != NULL) {
276                 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
277                         addmntent(new_mtab, mnt);
278                 }
279         }
280
281         endmntent(mtab);
282
283         if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
284                 DEBUG(0,("%d: Error changing mode of %s: %s\n",
285                          sys_getpid(), MOUNTED_TMP, strerror(errno)));
286                 return;
287         }
288
289         endmntent(new_mtab);
290
291         if (rename(MOUNTED_TMP, MOUNTED) < 0) {
292                 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
293                          sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
294                 return;
295         }
296
297         if (unlink(MOUNTED"~") == -1) {
298                 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
299                 return;
300         }
301 }
302
303
304 /*
305  * Call the smbfs ioctl to install a connection socket,
306  * then wait for a signal to reconnect. Note that we do
307  * not exit after open_sockets() or send_login() errors,
308  * as the smbfs mount would then have no way to recover.
309  */
310 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
311 {
312         int fd, closed = 0, res = 1;
313         pid_t parentpid = getppid();
314         struct smb_conn_opt conn_options;
315
316         memset(&conn_options, 0, sizeof(conn_options));
317
318         while (1) {
319                 if ((fd = open(mount_point, O_RDONLY)) < 0) {
320                         DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
321                                  sys_getpid(), mount_point));
322                         break;
323                 }
324
325                 conn_options.fd = c->fd;
326                 conn_options.protocol = c->protocol;
327                 conn_options.case_handling = SMB_CASE_DEFAULT;
328                 conn_options.max_xmit = c->max_xmit;
329                 conn_options.server_uid = c->vuid;
330                 conn_options.tid = c->cnum;
331                 conn_options.secmode = c->sec_mode;
332                 conn_options.rawmode = 0;
333                 conn_options.sesskey = c->sesskey;
334                 conn_options.maxraw = 0;
335                 conn_options.capabilities = c->capabilities;
336                 conn_options.serverzone = c->serverzone/60;
337
338                 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
339                 if (res != 0) {
340                         DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
341                                  sys_getpid(), res));
342                         close(fd);
343                         break;
344                 }
345
346                 if (parentpid) {
347                         /* Ok...  We are going to kill the parent.  Now
348                                 is the time to break the process group... */
349                         setsid();
350                         /* Send a signal to the parent to terminate */
351                         kill(parentpid, SIGTERM);
352                         parentpid = 0;
353                 }
354
355                 close(fd);
356
357                 /* This looks wierd but we are only closing the userspace
358                    side, the connection has already been passed to smbfs and 
359                    it has increased the usage count on the socket.
360
361                    If we don't do this we will "leak" sockets and memory on
362                    each reconnection we have to make. */
363                 cli_shutdown(c);
364                 c = NULL;
365
366                 if (!closed) {
367                         /* redirect stdout & stderr since we can't know that
368                            the library functions we use are using DEBUG. */
369                         if ( (fd = open("/dev/null", O_WRONLY)) < 0)
370                                 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
371                         close_our_files(fd);
372                         if (fd >= 0) {
373                                 dup2(fd, STDOUT_FILENO);
374                                 dup2(fd, STDERR_FILENO);
375                                 close(fd);
376                         }
377
378                         /* here we are no longer interactive */
379                         set_remote_machine_name("smbmount");    /* sneaky ... */
380                         setup_logging("mount.smbfs", False);
381                         append_log = True;
382                         reopen_logs();
383                         DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
384
385                         closed = 1;
386                 }
387
388                 /* Wait for a signal from smbfs ... but don't continue
389                    until we actually get a new connection. */
390                 while (!c) {
391                         CatchSignal(SIGUSR1, &usr1_handler);
392                         pause();
393                         DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
394                         c = do_connection(the_service);
395                 }
396         }
397
398         smb_umount(mount_point);
399         DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
400         exit(1);
401 }
402
403
404 /**
405  * Mount a smbfs
406  **/
407 static void init_mount(void)
408 {
409         char mount_point[MAXPATHLEN+1];
410         pstring tmp;
411         pstring svc2;
412         struct cli_state *c;
413         char *args[20];
414         int i, status;
415
416         if (realpath(mpoint, mount_point) == NULL) {
417                 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
418                 return;
419         }
420
421
422         c = do_connection(service);
423         if (!c) {
424                 fprintf(stderr,"SMB connection failed\n");
425                 exit(1);
426         }
427
428         /*
429                 Set up to return as a daemon child and wait in the parent
430                 until the child say it's ready...
431         */
432         daemonize();
433
434         pstrcpy(svc2, service);
435         string_replace(svc2, '\\','/');
436         string_replace(svc2, ' ','_');
437
438         memset(args, 0, sizeof(args[0])*20);
439
440         i=0;
441         args[i++] = "smbmnt";
442
443         args[i++] = mount_point;
444         args[i++] = "-s";
445         args[i++] = svc2;
446
447         if (mount_ro) {
448                 args[i++] = "-r";
449         }
450         if (mount_uid) {
451                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
452                 args[i++] = "-u";
453                 args[i++] = smb_xstrdup(tmp);
454         }
455         if (mount_gid) {
456                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
457                 args[i++] = "-g";
458                 args[i++] = smb_xstrdup(tmp);
459         }
460         if (mount_fmask) {
461                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
462                 args[i++] = "-f";
463                 args[i++] = smb_xstrdup(tmp);
464         }
465         if (mount_dmask) {
466                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
467                 args[i++] = "-d";
468                 args[i++] = smb_xstrdup(tmp);
469         }
470         if (options) {
471                 args[i++] = "-o";
472                 args[i++] = options;
473         }
474
475         if (sys_fork() == 0) {
476                 char *smbmnt_path;
477
478                 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
479                 
480                 if (file_exist(smbmnt_path, NULL)) {
481                         execv(smbmnt_path, args);
482                         fprintf(stderr,
483                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
484                                 smbmnt_path, strerror(errno));
485                 } else {
486                         execvp("smbmnt", args);
487                         fprintf(stderr,
488                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
489                                 "smbmnt", strerror(errno));
490                 }
491                 free(smbmnt_path);
492                 exit(1);
493         }
494
495         if (waitpid(-1, &status, 0) == -1) {
496                 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
497                 /* FIXME: do some proper error handling */
498                 exit(1);
499         }
500
501         if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
502                 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
503                 /* FIXME: do some proper error handling */
504                 exit(1);
505         }
506
507         /* Ok...  This is the rubicon for that mount point...  At any point
508            after this, if the connections fail and can not be reconstructed
509            for any reason, we will have to unmount the mount point.  There
510            is no exit from the next call...
511         */
512         send_fs_socket(service, mount_point, c);
513 }
514
515
516 /****************************************************************************
517 get a password from a a file or file descriptor
518 exit on failure (from smbclient, move to libsmb or shared .c file?)
519 ****************************************************************************/
520 static void get_password_file(void)
521 {
522         int fd = -1;
523         char *p;
524         BOOL close_it = False;
525         pstring spec;
526         char pass[128];
527
528         if ((p = getenv("PASSWD_FD")) != NULL) {
529                 pstrcpy(spec, "descriptor ");
530                 pstrcat(spec, p);
531                 sscanf(p, "%d", &fd);
532                 close_it = False;
533         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
534                 fd = sys_open(p, O_RDONLY, 0);
535                 pstrcpy(spec, p);
536                 if (fd < 0) {
537                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
538                                 spec, strerror(errno));
539                         exit(1);
540                 }
541                 close_it = True;
542         }
543
544         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
545             p && p - pass < sizeof(pass);) {
546                 switch (read(fd, p, 1)) {
547                 case 1:
548                         if (*p != '\n' && *p != '\0') {
549                                 *++p = '\0'; /* advance p, and null-terminate pass */
550                                 break;
551                         }
552                 case 0:
553                         if (p - pass) {
554                                 *p = '\0'; /* null-terminate it, just in case... */
555                                 p = NULL; /* then force the loop condition to become false */
556                                 break;
557                         } else {
558                                 fprintf(stderr, "Error reading password from file %s: %s\n",
559                                         spec, "empty password\n");
560                                 exit(1);
561                         }
562
563                 default:
564                         fprintf(stderr, "Error reading password from file %s: %s\n",
565                                 spec, strerror(errno));
566                         exit(1);
567                 }
568         }
569         pstrcpy(password, pass);
570         if (close_it)
571                 close(fd);
572 }
573
574 /****************************************************************************
575 get username and password from a credentials file
576 exit on failure (from smbclient, move to libsmb or shared .c file?)
577 ****************************************************************************/
578 static void read_credentials_file(char *filename)
579 {
580         FILE *auth;
581         fstring buf;
582         uint16 len = 0;
583         char *ptr, *val, *param;
584
585         if ((auth=sys_fopen(filename, "r")) == NULL)
586         {
587                 /* fail if we can't open the credentials file */
588                 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
589                 exit (-1);
590         }
591
592         while (!feof(auth))
593         {
594                 /* get a line from the file */
595                 if (!fgets (buf, sizeof(buf), auth))
596                         continue;
597                 len = strlen(buf);
598
599                 if ((len) && (buf[len-1]=='\n'))
600                 {
601                         buf[len-1] = '\0';
602                         len--;
603                 }
604                 if (len == 0)
605                         continue;
606
607                 /* break up the line into parameter & value.
608                    will need to eat a little whitespace possibly */
609                 param = buf;
610                 if (!(ptr = strchr (buf, '=')))
611                         continue;
612                 val = ptr+1;
613                 *ptr = '\0';
614
615                 /* eat leading white space */
616                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
617                         val++;
618
619                 if (strwicmp("password", param) == 0)
620                 {
621                         pstrcpy(password, val);
622                         got_pass = True;
623                 }
624                 else if (strwicmp("username", param) == 0)
625                         pstrcpy(username, val);
626
627                 memset(buf, 0, sizeof(buf));
628         }
629         fclose(auth);
630 }
631
632
633 /****************************************************************************
634 usage on the program
635 ****************************************************************************/
636 static void usage(void)
637 {
638         printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
639
640         printf("Version %s\n\n",VERSION);
641
642         printf(
643 "Options:\n\
644       username=<arg>                  SMB username\n\
645       password=<arg>                  SMB password\n\
646       credentials=<filename>          file with username/password\n\
647       netbiosname=<arg>               source NetBIOS name\n\
648       uid=<arg>                       mount uid or username\n\
649       gid=<arg>                       mount gid or groupname\n\
650       port=<arg>                      remote SMB port number\n\
651       fmask=<arg>                     file umask\n\
652       dmask=<arg>                     directory umask\n\
653       debug=<arg>                     debug level\n\
654       ip=<arg>                        destination host or IP address\n\
655       workgroup=<arg>                 workgroup on destination\n\
656       sockopt=<arg>                   TCP socket options\n\
657       scope=<arg>                     NetBIOS scope\n\
658       iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
659       codepage=<arg>                  server codepage (cp850)\n\
660       ttl=<arg>                       dircache time to live\n\
661       guest                           don't prompt for a password\n\
662       ro                              mount read-only\n\
663       rw                              mount read-write\n\
664 \n\
665 This command is designed to be run from within /bin/mount by giving\n\
666 the option '-t smbfs'. For example:\n\
667   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
668 ");
669 }
670
671
672 /****************************************************************************
673   Argument parsing for mount.smbfs interface
674   mount will call us like this:
675     mount.smbfs device mountpoint -o <options>
676   
677   <options> is never empty, containing at least rw or ro
678  ****************************************************************************/
679 static void parse_mount_smb(int argc, char **argv)
680 {
681         int opt;
682         char *opts;
683         char *opteq;
684         extern char *optarg;
685         int val;
686         extern pstring global_scope;
687         char *p;
688
689         if (argc < 2 || argv[1][0] == '-') {
690                 usage();
691                 exit(1);
692         }
693         
694         pstrcpy(service, argv[1]);
695         pstrcpy(mpoint, argv[2]);
696
697         /* Convert any '/' characters in the service name to
698            '\' characters */
699         string_replace(service, '/','\\');
700         argc -= 2;
701         argv += 2;
702
703         opt = getopt(argc, argv, "o:");
704         if(opt != 'o') {
705                 return;
706         }
707
708         options[0] = 0;
709         p = options;
710
711         /*
712          * option parsing from nfsmount.c (util-linux-2.9u)
713          */
714         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
715                 DEBUG(3, ("opts: %s\n", opts));
716                 if ((opteq = strchr_m(opts, '='))) {
717                         val = atoi(opteq + 1);
718                         *opteq = '\0';
719
720                         if (!strcmp(opts, "username") || 
721                             !strcmp(opts, "logon")) {
722                                 char *lp;
723                                 pstrcpy(username,opteq+1);
724                                 if ((lp=strchr_m(username,'%'))) {
725                                         *lp = 0;
726                                         pstrcpy(password,lp+1);
727                                         got_pass = True;
728                                         memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
729                                 }
730                                 if ((lp=strchr_m(username,'/'))) {
731                                         *lp = 0;
732                                         pstrcpy(workgroup,lp+1);
733                                 }
734                         } else if(!strcmp(opts, "passwd") ||
735                                   !strcmp(opts, "password")) {
736                                 pstrcpy(password,opteq+1);
737                                 got_pass = True;
738                                 memset(opteq+1,'X',strlen(password));
739                         } else if(!strcmp(opts, "credentials")) {
740                                 pstrcpy(credentials,opteq+1);
741                         } else if(!strcmp(opts, "netbiosname")) {
742                                 pstrcpy(my_netbios_name,opteq+1);
743                         } else if(!strcmp(opts, "uid")) {
744                                 mount_uid = nametouid(opteq+1);
745                         } else if(!strcmp(opts, "gid")) {
746                                 mount_gid = nametogid(opteq+1);
747                         } else if(!strcmp(opts, "port")) {
748                                 smb_port = val;
749                         } else if(!strcmp(opts, "fmask")) {
750                                 mount_fmask = strtol(opteq+1, NULL, 8);
751                         } else if(!strcmp(opts, "dmask")) {
752                                 mount_dmask = strtol(opteq+1, NULL, 8);
753                         } else if(!strcmp(opts, "debug")) {
754                                 DEBUGLEVEL = val;
755                         } else if(!strcmp(opts, "ip")) {
756                                 dest_ip = *interpret_addr2(opteq+1);
757                                 if (is_zero_ip(dest_ip)) {
758                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
759                                         exit(1);
760                                 }
761                                 have_ip = True;
762                         } else if(!strcmp(opts, "workgroup")) {
763                                 pstrcpy(workgroup,opteq+1);
764                         } else if(!strcmp(opts, "sockopt")) {
765                                 pstrcpy(user_socket_options,opteq+1);
766                         } else if(!strcmp(opts, "scope")) {
767                                 pstrcpy(global_scope,opteq+1);
768                         } else {
769                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
770                                 p += strlen(p);
771                         }
772                 } else {
773                         val = 1;
774                         if(!strcmp(opts, "nocaps")) {
775                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
776                                 exit(1);
777                         } else if(!strcmp(opts, "guest")) {
778                                 *password = '\0';
779                                 got_pass = True;
780                         } else if(!strcmp(opts, "rw")) {
781                                 mount_ro = 0;
782                         } else if(!strcmp(opts, "ro")) {
783                                 mount_ro = 1;
784                         } else {
785                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
786                                 p += strlen(opts);
787                                 *p++ = ',';
788                                 *p = 0;
789                         }
790                 }
791         }
792
793         if (!*service) {
794                 usage();
795                 exit(1);
796         }
797
798         if (p != options) {
799                 *(p-1) = 0;     /* remove trailing , */
800                 DEBUG(3,("passthrough options '%s'\n", options));
801         }
802 }
803
804 /****************************************************************************
805   main program
806 ****************************************************************************/
807  int main(int argc,char *argv[])
808 {
809         extern char *optarg;
810         extern int optind;
811         char *p;
812
813         DEBUGLEVEL = 1;
814
815         /* here we are interactive, even if run from autofs */
816         setup_logging("mount.smbfs",True);
817
818 #if 0 /* JRA - Urban says not needed ? */
819         /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
820            is to not announce any unicode capabilities as current smbfs does
821            not support it. */
822         p = getenv("CLI_FORCE_ASCII");
823         if (p && !strcmp(p, "false"))
824                 unsetenv("CLI_FORCE_ASCII");
825         else
826                 setenv("CLI_FORCE_ASCII", "true", 1);
827 #endif
828
829         in_client = True;   /* Make sure that we tell lp_load we are */
830
831         if (getenv("USER")) {
832                 pstrcpy(username,getenv("USER"));
833
834                 if ((p=strchr_m(username,'%'))) {
835                         *p = 0;
836                         pstrcpy(password,p+1);
837                         got_pass = True;
838                         memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
839                 }
840                 strupper(username);
841         }
842
843         if (getenv("PASSWD")) {
844                 pstrcpy(password,getenv("PASSWD"));
845                 got_pass = True;
846         }
847
848         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
849                 get_password_file();
850                 got_pass = True;
851         }
852
853         if (*username == 0 && getenv("LOGNAME")) {
854                 pstrcpy(username,getenv("LOGNAME"));
855         }
856
857         if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
858                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
859                         dyn_CONFIGFILE);
860         }
861
862         parse_mount_smb(argc, argv);
863
864         if (*credentials != 0) {
865                 read_credentials_file(credentials);
866         }
867
868         DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
869
870         if (*workgroup == 0) {
871                 pstrcpy(workgroup,lp_workgroup());
872         }
873
874         load_interfaces();
875         if (!*my_netbios_name) {
876                 pstrcpy(my_netbios_name, myhostname());
877         }
878         strupper(my_netbios_name);
879
880         init_mount();
881         return 0;
882 }