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