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