mount.cifs: print output to stderr
[abartlet/samba.git/.git] / client / mount.cifs.c
1 /* 
2    Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3    Copyright (C) 2003,2008 Steve French  (sfrench@us.ibm.com)
4    Copyright (C) 2008 Jeremy Allison (jra@samba.org)
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/mount.h>
31 #include <sys/stat.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <getopt.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <mntent.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <fstab.h>
43 #include "mount.h"
44
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "13"
47
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49  #ifdef _SAMBA_BUILD_
50   #include "version.h"
51   #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53   #else
54    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55   #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56  #else
57    #define MOUNT_CIFS_VENDOR_SUFFIX ""
58  #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
60
61 #ifdef _SAMBA_BUILD_
62 #include "include/config.h"
63 #endif
64
65 #ifndef MS_MOVE 
66 #define MS_MOVE 8192 
67 #endif 
68
69 #ifndef MS_BIND
70 #define MS_BIND 4096
71 #endif
72
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS        0x40000000
75 #define MS_USER         0x80000000
76
77 #define MAX_UNC_LEN 1024
78
79 #define CONST_DISCARD(type, ptr)      ((type) ((void *) (ptr)))
80
81 #ifndef SAFE_FREE
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
83 #endif
84
85 #define MOUNT_PASSWD_SIZE 128
86 #define DOMAIN_SIZE 64
87
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
90
91 /*
92  * By default, mount.cifs follows the conventions set forth by /bin/mount
93  * for user mounts. That is, it requires that the mount be listed in
94  * /etc/fstab with the "user" option when run as an unprivileged user and
95  * mount.cifs is setuid root.
96  *
97  * Older versions of mount.cifs however were "looser" in this regard. When
98  * made setuid root, a user could run mount.cifs directly and mount any share
99  * on a directory owned by that user.
100  *
101  * The legacy behavior is now disabled by default. To reenable it, set the
102  * following #define to true.
103  */
104 #define CIFS_LEGACY_SETUID_CHECK 0
105
106 /*
107  * When an unprivileged user runs a setuid mount.cifs, we set certain mount
108  * flags by default. These defaults can be changed here.
109  */
110 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
111
112 const char *thisprogram;
113 int verboseflag = 0;
114 int fakemnt = 0;
115 static int got_password = 0;
116 static int got_user = 0;
117 static int got_domain = 0;
118 static int got_ip = 0;
119 static int got_unc = 0;
120 static int got_uid = 0;
121 static int got_gid = 0;
122 static char * user_name = NULL;
123 static char * mountpassword = NULL;
124 char * domain_name = NULL;
125 char * prefixpath = NULL;
126
127 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
128  * don't link to libreplace so need them here. */
129
130 /* like strncpy but does not 0 fill the buffer and always null
131  *    terminates. bufsize is the size of the destination buffer */
132
133 #ifndef HAVE_STRLCPY
134 static size_t strlcpy(char *d, const char *s, size_t bufsize)
135 {
136         size_t len = strlen(s);
137         size_t ret = len;
138         if (bufsize <= 0) return 0;
139         if (len >= bufsize) len = bufsize-1;
140         memcpy(d, s, len);
141         d[len] = 0;
142         return ret;
143 }
144 #endif
145
146 /* like strncat but does not 0 fill the buffer and always null
147  *    terminates. bufsize is the length of the buffer, which should
148  *       be one more than the maximum resulting string length */
149
150 #ifndef HAVE_STRLCAT
151 static size_t strlcat(char *d, const char *s, size_t bufsize)
152 {
153         size_t len1 = strlen(d);
154         size_t len2 = strlen(s);
155         size_t ret = len1 + len2;
156
157         if (len1+len2 >= bufsize) {
158                 if (bufsize < (len1+1)) {
159                         return ret;
160                 }
161                 len2 = bufsize - (len1+1);
162         }
163         if (len2 > 0) {
164                 memcpy(d+len1, s, len2);
165                 d[len1+len2] = 0;
166         }
167         return ret;
168 }
169 #endif
170
171 /*
172  * If an unprivileged user is doing the mounting then we need to ensure
173  * that the entry is in /etc/fstab.
174  */
175 static int
176 check_mountpoint(const char *progname, char *mountpoint)
177 {
178         int err;
179         struct stat statbuf;
180
181         /* does mountpoint exist and is it a directory? */
182         err = stat(mountpoint, &statbuf);
183         if (err) {
184                 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
185                                 mountpoint, strerror(errno));
186                 return EX_USAGE;
187         }
188
189         if (!S_ISDIR(statbuf.st_mode)) {
190                 fprintf(stderr, "%s: %s is not a directory!", progname,
191                                 mountpoint);
192                 return EX_USAGE;
193         }
194
195 #if CIFS_LEGACY_SETUID_CHECK
196         /* do extra checks on mountpoint for legacy setuid behavior */
197         if (!getuid() || geteuid())
198                 return 0;
199
200         if (statbuf.st_uid != getuid()) {
201                 fprintf(stderr, "%s: %s is not owned by user\n", progname,
202                         mountpoint);
203                 return EX_USAGE;
204         }
205
206         if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
207                 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
208                         mountpoint);
209                 return EX_USAGE;
210         }
211 #endif /* CIFS_LEGACY_SETUID_CHECK */
212
213         return 0;
214 }
215
216 #if CIFS_LEGACY_SETUID_CHECK
217 static int
218 check_fstab(const char *progname, char *mountpoint, char *devname,
219             char **options)
220 {
221         return 0;
222 }
223 #else /* CIFS_LEGACY_SETUID_CHECK */
224 static int
225 check_fstab(const char *progname, char *mountpoint, char *devname,
226             char **options)
227 {
228         FILE *fstab;
229         struct mntent *mnt;
230
231         /* make sure this mount is listed in /etc/fstab */
232         fstab = setmntent(_PATH_FSTAB, "r");
233         if (!fstab) {
234                 fprintf(stderr, "Couldn't open %s for reading!\n",
235                                 _PATH_FSTAB);
236                 return EX_FILEIO;
237         }
238
239         while((mnt = getmntent(fstab))) {
240                 if (!strcmp(mountpoint, mnt->mnt_dir))
241                         break;
242         }
243         endmntent(fstab);
244
245         if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
246                 fprintf(stderr, "%s: permission denied: no match for "
247                                 "%s found in %s\n", progname, mountpoint,
248                                 _PATH_FSTAB);
249                 return EX_USAGE;
250         }
251
252         /*
253          * 'mount' munges the options from fstab before passing them
254          * to us. It is non-trivial to test that we have the correct
255          * set of options. We don't want to trust what the user
256          * gave us, so just take whatever is in /etc/fstab.
257          */
258         free(*options);
259         *options = strdup(mnt->mnt_opts);
260         return 0;
261 }
262 #endif /* CIFS_LEGACY_SETUID_CHECK */
263
264 /* BB finish BB
265
266         cifs_umount
267         open nofollow - avoid symlink exposure? 
268         get owner of dir see if matches self or if root
269         call system(umount argv) etc.
270                 
271 BB end finish BB */
272
273 static char * check_for_domain(char **);
274
275
276 static void mount_cifs_usage(FILE *stream)
277 {
278         fprintf(stream, "\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
279         fprintf(stream, "\nMount the remote target, specified as a UNC name,");
280         fprintf(stream, " to a local directory.\n\nOptions:\n");
281         fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282         fprintf(stream, "\nLess commonly used options:");
283         fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284         fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285         fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286         fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287         fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
288         fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
289         fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290         fprintf(stream, "\n\nRarely used options:");
291         fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292         fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293         fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294         fprintf(stream, "\n\nOptions are described in more detail in the manual page");
295         fprintf(stream, "\n\tman 8 mount.cifs\n");
296         fprintf(stream, "\nTo display the version number of the mount helper:");
297         fprintf(stream, "\n\t%s -V\n",thisprogram);
298
299         SAFE_FREE(mountpassword);
300
301         if (stream == stderr)
302                 exit(EX_USAGE);
303         exit(0);
304 }
305
306 /* caller frees username if necessary */
307 static char * getusername(void) {
308         char *username = NULL;
309         struct passwd *password = getpwuid(getuid());
310
311         if (password) {
312                 username = password->pw_name;
313         }
314         return username;
315 }
316
317 static int open_cred_file(char * file_name)
318 {
319         char * line_buf;
320         char * temp_val;
321         FILE * fs;
322         int i, length;
323         fs = fopen(file_name,"r");
324         if(fs == NULL)
325                 return errno;
326         line_buf = (char *)malloc(4096);
327         if(line_buf == NULL) {
328                 fclose(fs);
329                 return ENOMEM;
330         }
331
332         while(fgets(line_buf,4096,fs)) {
333                 /* parse line from credential file */
334
335                 /* eat leading white space */
336                 for(i=0;i<4086;i++) {
337                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
338                                 break;
339                         /* if whitespace - skip past it */
340                 }
341                 if (strncasecmp("username",line_buf+i,8) == 0) {
342                         temp_val = strchr(line_buf + i,'=');
343                         if(temp_val) {
344                                 /* go past equals sign */
345                                 temp_val++;
346                                 for(length = 0;length<4087;length++) {
347                                         if ((temp_val[length] == '\n')
348                                             || (temp_val[length] == '\0')) {
349                                                 temp_val[length] = '\0';
350                                                 break;
351                                         }
352                                 }
353                                 if(length > 4086) {
354                                         fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
355                                         memset(line_buf,0,4096);
356                                         exit(EX_USAGE);
357                                 } else {
358                                         got_user = 1;
359                                         user_name = (char *)calloc(1 + length,1);
360                                         /* BB adding free of user_name string before exit,
361                                                 not really necessary but would be cleaner */
362                                         strlcpy(user_name,temp_val, length+1);
363                                 }
364                         }
365                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
366                         temp_val = strchr(line_buf+i,'=');
367                         if(temp_val) {
368                                 /* go past equals sign */
369                                 temp_val++;
370                                 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
371                                         if ((temp_val[length] == '\n')
372                                             || (temp_val[length] == '\0')) {
373                                                 temp_val[length] = '\0';
374                                                 break;
375                                         }
376                                 }
377                                 if(length > MOUNT_PASSWD_SIZE) {
378                                         fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
379                                         memset(line_buf,0, 4096);
380                                         exit(EX_USAGE);
381                                 } else {
382                                         if(mountpassword == NULL) {
383                                                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
384                                         } else
385                                                 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
386                                         if(mountpassword) {
387                                                 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
388                                                 got_password = 1;
389                                         }
390                                 }
391                         }
392                 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
393                         temp_val = strchr(line_buf+i,'=');
394                         if(temp_val) {
395                                 /* go past equals sign */
396                                 temp_val++;
397                                 if(verboseflag)
398                                         fprintf(stderr, "\nDomain %s\n",temp_val);
399                                 for(length = 0;length<DOMAIN_SIZE+1;length++) {
400                                         if ((temp_val[length] == '\n')
401                                             || (temp_val[length] == '\0')) {
402                                                 temp_val[length] = '\0';
403                                                 break;
404                                         }
405                                 }
406                                 if(length > DOMAIN_SIZE) {
407                                         fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
408                                         exit(EX_USAGE);
409                                 } else {
410                                         if(domain_name == NULL) {
411                                                 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
412                                         } else
413                                                 memset(domain_name,0,DOMAIN_SIZE);
414                                         if(domain_name) {
415                                                 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
416                                                 got_domain = 1;
417                                         }
418                                 }
419                         }
420                 }
421
422         }
423         fclose(fs);
424         SAFE_FREE(line_buf);
425         return 0;
426 }
427
428 static int get_password_from_file(int file_descript, char * filename)
429 {
430         int rc = 0;
431         int i;
432         char c;
433
434         if(mountpassword == NULL)
435                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
436         else 
437                 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
438
439         if (mountpassword == NULL) {
440                 fprintf(stderr, "malloc failed\n");
441                 exit(EX_SYSERR);
442         }
443
444         if(filename != NULL) {
445                 file_descript = open(filename, O_RDONLY);
446                 if(file_descript < 0) {
447                         fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
448                                    strerror(errno),filename);
449                         exit(EX_SYSERR);
450                 }
451         }
452         /* else file already open and fd provided */
453
454         for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
455                 rc = read(file_descript,&c,1);
456                 if(rc < 0) {
457                         fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
458                         if(filename != NULL)
459                                 close(file_descript);
460                         exit(EX_SYSERR);
461                 } else if(rc == 0) {
462                         if(mountpassword[0] == 0) {
463                                 if(verboseflag)
464                                         fprintf(stderr, "\nWarning: null password used since cifs password file empty");
465                         }
466                         break;
467                 } else /* read valid character */ {
468                         if((c == 0) || (c == '\n')) {
469                                 mountpassword[i] = '\0';
470                                 break;
471                         } else 
472                                 mountpassword[i] = c;
473                 }
474         }
475         if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
476                 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
477                         MOUNT_PASSWD_SIZE);
478         }
479         got_password = 1;
480         if(filename != NULL) {
481                 close(file_descript);
482         }
483
484         return rc;
485 }
486
487 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
488 {
489         const char * data;
490         char * percent_char = NULL;
491         char * value = NULL;
492         char * next_keyword = NULL;
493         char * out = NULL;
494         int out_len = 0;
495         int word_len;
496         int rc = 0;
497         char user[32];
498         char group[32];
499
500         if (!optionsp || !*optionsp)
501                 return 1;
502         data = *optionsp;
503
504         if(verboseflag)
505                 fprintf(stderr, "parsing options: %s\n", data);
506
507         /* BB fixme check for separator override BB */
508
509         if (getuid()) {
510                 got_uid = 1;
511                 snprintf(user,sizeof(user),"%u",getuid());
512                 got_gid = 1;
513                 snprintf(group,sizeof(group),"%u",getgid());
514         }
515
516 /* while ((data = strsep(&options, ",")) != NULL) { */
517         while(data != NULL) {
518                 /*  check if ends with trailing comma */
519                 if(*data == 0)
520                         break;
521
522                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
523                 /* data  = next keyword */
524                 /* value = next value ie stuff after equal sign */
525
526                 next_keyword = strchr(data,','); /* BB handle sep= */
527         
528                 /* temporarily null terminate end of keyword=value pair */
529                 if(next_keyword)
530                         *next_keyword++ = 0;
531
532                 /* temporarily null terminate keyword to make keyword and value distinct */
533                 if ((value = strchr(data, '=')) != NULL) {
534                         *value = '\0';
535                         value++;
536                 }
537
538                 if (strncmp(data, "users",5) == 0) {
539                         if(!value || !*value) {
540                                 *filesys_flags |= MS_USERS;
541                                 goto nocopy;
542                         }
543                 } else if (strncmp(data, "user_xattr",10) == 0) {
544                    /* do nothing - need to skip so not parsed as user name */
545                 } else if (strncmp(data, "user", 4) == 0) {
546
547                         if (!value || !*value) {
548                                 if(data[4] == '\0') {
549                                         *filesys_flags |= MS_USER;
550                                         goto nocopy;
551                                 } else {
552                                         fprintf(stderr, "username specified with no parameter\n");
553                                         SAFE_FREE(out);
554                                         return 1;       /* needs_arg; */
555                                 }
556                         } else {
557                                 if (strnlen(value, 260) < 260) {
558                                         got_user=1;
559                                         percent_char = strchr(value,'%');
560                                         if(percent_char) {
561                                                 *percent_char = ',';
562                                                 if(mountpassword == NULL)
563                                                         mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
564                                                 if(mountpassword) {
565                                                         if(got_password)
566                                                                 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
567                                                         got_password = 1;
568                                                         percent_char++;
569                                                         strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
570                                                 /*  remove password from username */
571                                                         while(*percent_char != 0) {
572                                                                 *percent_char = ',';
573                                                                 percent_char++;
574                                                         }
575                                                 }
576                                         }
577                                         /* this is only case in which the user
578                                         name buf is not malloc - so we have to
579                                         check for domain name embedded within
580                                         the user name here since the later
581                                         call to check_for_domain will not be
582                                         invoked */
583                                         domain_name = check_for_domain(&value);
584                                 } else {
585                                         fprintf(stderr, "username too long\n");
586                                         SAFE_FREE(out);
587                                         return 1;
588                                 }
589                         }
590                 } else if (strncmp(data, "pass", 4) == 0) {
591                         if (!value || !*value) {
592                                 if(got_password) {
593                                         fprintf(stderr, "\npassword specified twice, ignoring second\n");
594                                 } else
595                                         got_password = 1;
596                         } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
597                                 if(got_password)
598                                         fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
599                                 got_password = 1;
600                         } else {
601                                 fprintf(stderr, "password too long\n");
602                                 SAFE_FREE(out);
603                                 return 1;
604                         }
605                 } else if (strncmp(data, "sec", 3) == 0) {
606                         if (value) {
607                                 if (!strncmp(value, "none", 4) ||
608                                     !strncmp(value, "krb5", 4))
609                                         got_password = 1;
610                         }
611                 } else if (strncmp(data, "ip", 2) == 0) {
612                         if (!value || !*value) {
613                                 fprintf(stderr, "target ip address argument missing");
614                         } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
615                                 if(verboseflag)
616                                         fprintf(stderr, "ip address %s override specified\n",value);
617                                 got_ip = 1;
618                         } else {
619                                 fprintf(stderr, "ip address too long\n");
620                                 SAFE_FREE(out);
621                                 return 1;
622                         }
623                 } else if ((strncmp(data, "unc", 3) == 0)
624                    || (strncmp(data, "target", 6) == 0)
625                    || (strncmp(data, "path", 4) == 0)) {
626                         if (!value || !*value) {
627                                 fprintf(stderr, "invalid path to network resource\n");
628                                 SAFE_FREE(out);
629                                 return 1;  /* needs_arg; */
630                         } else if(strnlen(value,5) < 5) {
631                                 fprintf(stderr, "UNC name too short");
632                         }
633
634                         if (strnlen(value, 300) < 300) {
635                                 got_unc = 1;
636                                 if (strncmp(value, "//", 2) == 0) {
637                                         if(got_unc)
638                                                 fprintf(stderr, "unc name specified twice, ignoring second\n");
639                                         else
640                                                 got_unc = 1;
641                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
642                                         fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
643                                         SAFE_FREE(out);
644                                         return 1;
645                                 } else {
646                                         if(got_unc)
647                                                 fprintf(stderr, "unc name specified twice, ignoring second\n");
648                                         else
649                                                 got_unc = 1;
650                                 }
651                         } else {
652                                 fprintf(stderr, "CIFS: UNC name too long\n");
653                                 SAFE_FREE(out);
654                                 return 1;
655                         }
656                 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
657                            || (strncmp(data, "workg", 5) == 0)) {
658                         /* note this allows for synonyms of "domain"
659                            such as "DOM" and "dom" and "workgroup"
660                            and "WORKGRP" etc. */
661                         if (!value || !*value) {
662                                 fprintf(stderr, "CIFS: invalid domain name\n");
663                                 SAFE_FREE(out);
664                                 return 1;       /* needs_arg; */
665                         }
666                         if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
667                                 got_domain = 1;
668                         } else {
669                                 fprintf(stderr, "domain name too long\n");
670                                 SAFE_FREE(out);
671                                 return 1;
672                         }
673                 } else if (strncmp(data, "cred", 4) == 0) {
674                         if (value && *value) {
675                                 rc = open_cred_file(value);
676                                 if(rc) {
677                                         fprintf(stderr, "error %d (%s) opening credential file %s\n",
678                                                 rc, strerror(rc), value);
679                                         SAFE_FREE(out);
680                                         return 1;
681                                 }
682                         } else {
683                                 fprintf(stderr, "invalid credential file name specified\n");
684                                 SAFE_FREE(out);
685                                 return 1;
686                         }
687                 } else if (strncmp(data, "uid", 3) == 0) {
688                         if (value && *value) {
689                                 got_uid = 1;
690                                 if (!isdigit(*value)) {
691                                         struct passwd *pw;
692
693                                         if (!(pw = getpwnam(value))) {
694                                                 fprintf(stderr, "bad user name \"%s\"\n", value);
695                                                 exit(EX_USAGE);
696                                         }
697                                         snprintf(user, sizeof(user), "%u", pw->pw_uid);
698                                 } else {
699                                         strlcpy(user,value,sizeof(user));
700                                 }
701                         }
702                         goto nocopy;
703                 } else if (strncmp(data, "gid", 3) == 0) {
704                         if (value && *value) {
705                                 got_gid = 1;
706                                 if (!isdigit(*value)) {
707                                         struct group *gr;
708
709                                         if (!(gr = getgrnam(value))) {
710                                                 fprintf(stderr, "bad group name \"%s\"\n", value);
711                                                 exit(EX_USAGE);
712                                         }
713                                         snprintf(group, sizeof(group), "%u", gr->gr_gid);
714                                 } else {
715                                         strlcpy(group,value,sizeof(group));
716                                 }
717                         }
718                         goto nocopy;
719        /* fmask and dmask synonyms for people used to smbfs syntax */
720                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
721                         if (!value || !*value) {
722                                 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
723                                 SAFE_FREE(out);
724                                 return 1;
725                         }
726
727                         if (value[0] != '0') {
728                                 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
729                         }
730
731                         if (strcmp (data, "fmask") == 0) {
732                                 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
733                                 data = "file_mode"; /* BB fix this */
734                         }
735                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
736                         if (!value || !*value) {
737                                 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
738                                 SAFE_FREE(out);
739                                 return 1;
740                         }
741
742                         if (value[0] != '0') {
743                                 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
744                         }
745
746                         if (strcmp (data, "dmask") == 0) {
747                                 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
748                                 data = "dir_mode";
749                         }
750                         /* the following eight mount options should be
751                         stripped out from what is passed into the kernel
752                         since these eight options are best passed as the
753                         mount flags rather than redundantly to the kernel 
754                         and could generate spurious warnings depending on the
755                         level of the corresponding cifs vfs kernel code */
756                 } else if (strncmp(data, "nosuid", 6) == 0) {
757                         *filesys_flags |= MS_NOSUID;
758                 } else if (strncmp(data, "suid", 4) == 0) {
759                         *filesys_flags &= ~MS_NOSUID;
760                 } else if (strncmp(data, "nodev", 5) == 0) {
761                         *filesys_flags |= MS_NODEV;
762                 } else if ((strncmp(data, "nobrl", 5) == 0) || 
763                            (strncmp(data, "nolock", 6) == 0)) {
764                         *filesys_flags &= ~MS_MANDLOCK;
765                 } else if (strncmp(data, "dev", 3) == 0) {
766                         *filesys_flags &= ~MS_NODEV;
767                 } else if (strncmp(data, "noexec", 6) == 0) {
768                         *filesys_flags |= MS_NOEXEC;
769                 } else if (strncmp(data, "exec", 4) == 0) {
770                         *filesys_flags &= ~MS_NOEXEC;
771                 } else if (strncmp(data, "guest", 5) == 0) {
772                         user_name = (char *)calloc(1, 1);
773                         got_user = 1;
774                         got_password = 1;
775                 } else if (strncmp(data, "ro", 2) == 0) {
776                         *filesys_flags |= MS_RDONLY;
777                         goto nocopy;
778                 } else if (strncmp(data, "rw", 2) == 0) {
779                         *filesys_flags &= ~MS_RDONLY;
780                         goto nocopy;
781                 } else if (strncmp(data, "remount", 7) == 0) {
782                         *filesys_flags |= MS_REMOUNT;
783                 } /* else if (strnicmp(data, "port", 4) == 0) {
784                         if (value && *value) {
785                                 vol->port =
786                                         simple_strtoul(value, &value, 0);
787                         }
788                 } else if (strnicmp(data, "rsize", 5) == 0) {
789                         if (value && *value) {
790                                 vol->rsize =
791                                         simple_strtoul(value, &value, 0);
792                         }
793                 } else if (strnicmp(data, "wsize", 5) == 0) {
794                         if (value && *value) {
795                                 vol->wsize =
796                                         simple_strtoul(value, &value, 0);
797                         }
798                 } else if (strnicmp(data, "version", 3) == 0) {
799                 } else {
800                         fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
801                 } */ /* nothing to do on those four mount options above.
802                         Just pass to kernel and ignore them here */
803
804                 /* Copy (possibly modified) option to out */
805                 word_len = strlen(data);
806                 if (value)
807                         word_len += 1 + strlen(value);
808
809                 out = (char *)realloc(out, out_len + word_len + 2);
810                 if (out == NULL) {
811                         perror("malloc");
812                         exit(EX_SYSERR);
813                 }
814
815                 if (out_len) {
816                         strlcat(out, ",", out_len + word_len + 2);
817                         out_len++;
818                 }
819
820                 if (value)
821                         snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
822                 else
823                         snprintf(out + out_len, word_len + 1, "%s", data);
824                 out_len = strlen(out);
825
826 nocopy:
827                 data = next_keyword;
828         }
829
830         /* special-case the uid and gid */
831         if (got_uid) {
832                 word_len = strlen(user);
833
834                 out = (char *)realloc(out, out_len + word_len + 6);
835                 if (out == NULL) {
836                         perror("malloc");
837                         exit(EX_SYSERR);
838                 }
839
840                 if (out_len) {
841                         strlcat(out, ",", out_len + word_len + 6);
842                         out_len++;
843                 }
844                 snprintf(out + out_len, word_len + 5, "uid=%s", user);
845                 out_len = strlen(out);
846         }
847         if (got_gid) {
848                 word_len = strlen(group);
849
850                 out = (char *)realloc(out, out_len + 1 + word_len + 6);
851                 if (out == NULL) {
852                 perror("malloc");
853                         exit(EX_SYSERR);
854                 }
855
856                 if (out_len) {
857                         strlcat(out, ",", out_len + word_len + 6);
858                         out_len++;
859                 }
860                 snprintf(out + out_len, word_len + 5, "gid=%s", group);
861                 out_len = strlen(out);
862         }
863
864         SAFE_FREE(*optionsp);
865         *optionsp = out;
866         return 0;
867 }
868
869 /* replace all (one or more) commas with double commas */
870 static void check_for_comma(char ** ppasswrd)
871 {
872         char *new_pass_buf;
873         char *pass;
874         int i,j;
875         int number_of_commas = 0;
876         int len;
877
878         if(ppasswrd == NULL)
879                 return;
880         else 
881                 (pass = *ppasswrd);
882
883         len = strlen(pass);
884
885         for(i=0;i<len;i++)  {
886                 if(pass[i] == ',')
887                         number_of_commas++;
888         }
889
890         if(number_of_commas == 0)
891                 return;
892         if(number_of_commas > MOUNT_PASSWD_SIZE) {
893                 /* would otherwise overflow the mount options buffer */
894                 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
895                 return;
896         }
897
898         new_pass_buf = (char *)malloc(len+number_of_commas+1);
899         if(new_pass_buf == NULL)
900                 return;
901
902         for(i=0,j=0;i<len;i++,j++) {
903                 new_pass_buf[j] = pass[i];
904                 if(pass[i] == ',') {
905                         j++;
906                         new_pass_buf[j] = pass[i];
907                 }
908         }
909         new_pass_buf[len+number_of_commas] = 0;
910
911         SAFE_FREE(*ppasswrd);
912         *ppasswrd = new_pass_buf;
913         
914         return;
915 }
916
917 /* Usernames can not have backslash in them and we use
918    [BB check if usernames can have forward slash in them BB] 
919    backslash as domain\user separator character
920 */
921 static char * check_for_domain(char **ppuser)
922 {
923         char * original_string;
924         char * usernm;
925         char * domainnm;
926         int    original_len;
927         int    len;
928         int    i;
929
930         if(ppuser == NULL)
931                 return NULL;
932
933         original_string = *ppuser;
934
935         if (original_string == NULL)
936                 return NULL;
937         
938         original_len = strlen(original_string);
939
940         usernm = strchr(*ppuser,'/');
941         if (usernm == NULL) {
942                 usernm = strchr(*ppuser,'\\');
943                 if (usernm == NULL)
944                         return NULL;
945         }
946
947         if(got_domain) {
948                 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
949                 return NULL;
950         }
951
952         usernm[0] = 0;
953         domainnm = *ppuser;
954         if (domainnm[0] != 0) {
955                 got_domain = 1;
956         } else {
957                 fprintf(stderr, "null domain\n");
958         }
959         len = strlen(domainnm);
960         /* reset domainm to new buffer, and copy
961         domain name into it */
962         domainnm = (char *)malloc(len+1);
963         if(domainnm == NULL)
964                 return NULL;
965
966         strlcpy(domainnm,*ppuser,len+1);
967
968 /*      move_string(*ppuser, usernm+1) */
969         len = strlen(usernm+1);
970
971         if(len >= original_len) {
972                 /* should not happen */
973                 return domainnm;
974         }
975
976         for(i=0;i<original_len;i++) {
977                 if(i<len)
978                         original_string[i] = usernm[i+1];
979                 else /* stuff with commas to remove last parm */
980                         original_string[i] = ',';
981         }
982
983         /* BB add check for more than one slash? 
984           strchr(*ppuser,'/');
985           strchr(*ppuser,'\\') 
986         */
987         
988         return domainnm;
989 }
990
991 /* replace all occurances of "from" in a string with "to" */
992 static void replace_char(char *string, char from, char to, int maxlen)
993 {
994         char *lastchar = string + maxlen;
995         while (string) {
996                 string = strchr(string, from);
997                 if (string) {
998                         *string = to;
999                         if (string >= lastchar)
1000                                 return;
1001                 }
1002         }
1003 }
1004
1005 /* Note that caller frees the returned buffer if necessary */
1006 static struct addrinfo *
1007 parse_server(char ** punc_name)
1008 {
1009         char * unc_name = *punc_name;
1010         int length = strnlen(unc_name, MAX_UNC_LEN);
1011         char * share;
1012         struct addrinfo *addrlist;
1013         int rc;
1014
1015         if(length > (MAX_UNC_LEN - 1)) {
1016                 fprintf(stderr, "mount error: UNC name too long");
1017                 return NULL;
1018         }
1019         if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1020             (strncasecmp("smb://", unc_name, 6) == 0)) {
1021                 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1022                 return NULL;
1023         }
1024
1025         if(length < 3) {
1026                 /* BB add code to find DFS root here */
1027                 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1028                 return NULL;
1029         } else {
1030                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1031                         /* check for nfs syntax ie server:share */
1032                         share = strchr(unc_name,':');
1033                         if(share) {
1034                                 *punc_name = (char *)malloc(length+3);
1035                                 if(*punc_name == NULL) {
1036                                         /* put the original string back  if 
1037                                            no memory left */
1038                                         *punc_name = unc_name;
1039                                         return NULL;
1040                                 }
1041                                 *share = '/';
1042                                 strlcpy((*punc_name)+2,unc_name,length+1);
1043                                 SAFE_FREE(unc_name);
1044                                 unc_name = *punc_name;
1045                                 unc_name[length+2] = 0;
1046                                 goto continue_unc_parsing;
1047                         } else {
1048                                 fprintf(stderr, "mount error: improperly formatted UNC name.");
1049                                 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1050                                 return NULL;
1051                         }
1052                 } else {
1053 continue_unc_parsing:
1054                         unc_name[0] = '/';
1055                         unc_name[1] = '/';
1056                         unc_name += 2;
1057
1058                         /* allow for either delimiter between host and sharename */
1059                         if ((share = strpbrk(unc_name, "/\\"))) {
1060                                 *share = 0;  /* temporarily terminate the string */
1061                                 share += 1;
1062                                 if(got_ip == 0) {
1063                                         rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1064                                         if (rc != 0) {
1065                                                 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1066                                                         unc_name, gai_strerror(rc));
1067                                                 addrlist = NULL;
1068                                         }
1069                                 }
1070                                 *(share - 1) = '/'; /* put delimiter back */
1071
1072                                 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1073                                 if ((prefixpath = strpbrk(share, "/\\"))) {
1074                                         *prefixpath = 0;  /* permanently terminate the string */
1075                                         if (!strlen(++prefixpath))
1076                                                 prefixpath = NULL; /* this needs to be done explicitly */
1077                                 }
1078                                 if(got_ip) {
1079                                         if(verboseflag)
1080                                                 fprintf(stderr, "ip address specified explicitly\n");
1081                                         return NULL;
1082                                 }
1083                                 /* BB should we pass an alternate version of the share name as Unicode */
1084
1085                                 return addrlist; 
1086                         } else {
1087                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1088                                 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1089                                 return NULL;
1090                         }
1091                 }
1092         }
1093 }
1094
1095 static struct option longopts[] = {
1096         { "all", 0, NULL, 'a' },
1097         { "help",0, NULL, 'h' },
1098         { "move",0, NULL, 'm' },
1099         { "bind",0, NULL, 'b' },
1100         { "read-only", 0, NULL, 'r' },
1101         { "ro", 0, NULL, 'r' },
1102         { "verbose", 0, NULL, 'v' },
1103         { "version", 0, NULL, 'V' },
1104         { "read-write", 0, NULL, 'w' },
1105         { "rw", 0, NULL, 'w' },
1106         { "options", 1, NULL, 'o' },
1107         { "type", 1, NULL, 't' },
1108         { "rsize",1, NULL, 'R' },
1109         { "wsize",1, NULL, 'W' },
1110         { "uid", 1, NULL, '1'},
1111         { "gid", 1, NULL, '2'},
1112         { "user",1,NULL,'u'},
1113         { "username",1,NULL,'u'},
1114         { "dom",1,NULL,'d'},
1115         { "domain",1,NULL,'d'},
1116         { "password",1,NULL,'p'},
1117         { "pass",1,NULL,'p'},
1118         { "credentials",1,NULL,'c'},
1119         { "port",1,NULL,'P'},
1120         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1121         { NULL, 0, NULL, 0 }
1122 };
1123
1124 /* convert a string to uppercase. return false if the string
1125  * wasn't ASCII. Return success on a NULL ptr */
1126 static int
1127 uppercase_string(char *string)
1128 {
1129         if (!string)
1130                 return 1;
1131
1132         while (*string) {
1133                 /* check for unicode */
1134                 if ((unsigned char) string[0] & 0x80)
1135                         return 0;
1136                 *string = toupper((unsigned char) *string);
1137                 string++;
1138         }
1139
1140         return 1;
1141 }
1142
1143 static void print_cifs_mount_version(void)
1144 {
1145         printf("mount.cifs version: %s.%s%s\n",
1146                 MOUNT_CIFS_VERSION_MAJOR,
1147                 MOUNT_CIFS_VERSION_MINOR,
1148                 MOUNT_CIFS_VENDOR_SUFFIX);
1149 }
1150
1151 int main(int argc, char ** argv)
1152 {
1153         int c;
1154         unsigned long flags = MS_MANDLOCK;
1155         char * orgoptions = NULL;
1156         char * share_name = NULL;
1157         const char * ipaddr = NULL;
1158         char * uuid = NULL;
1159         char * mountpoint = NULL;
1160         char * options = NULL;
1161         char * optionstail;
1162         char * resolved_path = NULL;
1163         char * temp;
1164         char * dev_name;
1165         int rc = 0;
1166         int rsize = 0;
1167         int wsize = 0;
1168         int nomtab = 0;
1169         int uid = 0;
1170         int gid = 0;
1171         int optlen = 0;
1172         int orgoptlen = 0;
1173         size_t options_size = 0;
1174         size_t current_len;
1175         int retry = 0; /* set when we have to retry mount with uppercase */
1176         struct addrinfo *addrhead = NULL, *addr;
1177         struct utsname sysinfo;
1178         struct mntent mountent;
1179         struct sockaddr_in *addr4;
1180         struct sockaddr_in6 *addr6;
1181         FILE * pmntfile;
1182
1183         /* setlocale(LC_ALL, "");
1184         bindtextdomain(PACKAGE, LOCALEDIR);
1185         textdomain(PACKAGE); */
1186
1187         if(argc && argv)
1188                 thisprogram = argv[0];
1189         else
1190                 mount_cifs_usage(stderr);
1191
1192         if(thisprogram == NULL)
1193                 thisprogram = "mount.cifs";
1194
1195         uname(&sysinfo);
1196         /* BB add workstation name and domain and pass down */
1197
1198 /* #ifdef _GNU_SOURCE
1199         fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1200 #endif */
1201         if(argc > 2) {
1202                 dev_name = argv[1];
1203                 share_name = strndup(argv[1], MAX_UNC_LEN);
1204                 if (share_name == NULL) {
1205                         fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1206                         exit(EX_SYSERR);
1207                 }
1208                 mountpoint = argv[2];
1209         } else if (argc == 2) {
1210                 if ((strcmp(argv[1], "-V") == 0) ||
1211                     (strcmp(argv[1], "--version") == 0))
1212                 {
1213                         print_cifs_mount_version();
1214                         exit(0);
1215                 }
1216
1217                 if ((strcmp(argv[1], "-h") == 0) ||
1218                     (strcmp(argv[1], "-?") == 0) ||
1219                     (strcmp(argv[1], "--help") == 0))
1220                         mount_cifs_usage(stdout);
1221
1222                 mount_cifs_usage(stderr);
1223         } else {
1224                 mount_cifs_usage(stderr);
1225         }
1226
1227
1228         /* add sharename in opts string as unc= parm */
1229         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1230                          longopts, NULL)) != -1) {
1231                 switch (c) {
1232 /* No code to do the following  options yet */
1233 /*      case 'l':
1234                 list_with_volumelabel = 1;
1235                 break;
1236         case 'L':
1237                 volumelabel = optarg;
1238                 break; */
1239 /*      case 'a':              
1240                 ++mount_all;
1241                 break; */
1242
1243                 case '?':
1244                 case 'h':        /* help */
1245                         mount_cifs_usage(stdout);
1246                 case 'n':
1247                         ++nomtab;
1248                         break;
1249                 case 'b':
1250 #ifdef MS_BIND
1251                         flags |= MS_BIND;
1252 #else
1253                         fprintf(stderr,
1254                                 "option 'b' (MS_BIND) not supported\n");
1255 #endif
1256                         break;
1257                 case 'm':
1258 #ifdef MS_MOVE                
1259                         flags |= MS_MOVE;
1260 #else
1261                         fprintf(stderr,
1262                                 "option 'm' (MS_MOVE) not supported\n");
1263 #endif
1264                         break;
1265                 case 'o':
1266                         orgoptions = strdup(optarg);
1267                     break;
1268                 case 'r':  /* mount readonly */
1269                         flags |= MS_RDONLY;
1270                         break;
1271                 case 'U':
1272                         uuid = optarg;
1273                         break;
1274                 case 'v':
1275                         ++verboseflag;
1276                         break;
1277                 case 'V':
1278                         print_cifs_mount_version();
1279                         exit (0);
1280                 case 'w':
1281                         flags &= ~MS_RDONLY;
1282                         break;
1283                 case 'R':
1284                         rsize = atoi(optarg) ;
1285                         break;
1286                 case 'W':
1287                         wsize = atoi(optarg);
1288                         break;
1289                 case '1':
1290                         if (isdigit(*optarg)) {
1291                                 char *ep;
1292
1293                                 uid = strtoul(optarg, &ep, 10);
1294                                 if (*ep) {
1295                                         fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1296                                         exit(EX_USAGE);
1297                                 }
1298                         } else {
1299                                 struct passwd *pw;
1300
1301                                 if (!(pw = getpwnam(optarg))) {
1302                                         fprintf(stderr, "bad user name \"%s\"\n", optarg);
1303                                         exit(EX_USAGE);
1304                                 }
1305                                 uid = pw->pw_uid;
1306                                 endpwent();
1307                         }
1308                         break;
1309                 case '2':
1310                         if (isdigit(*optarg)) {
1311                                 char *ep;
1312
1313                                 gid = strtoul(optarg, &ep, 10);
1314                                 if (*ep) {
1315                                         fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1316                                         exit(EX_USAGE);
1317                                 }
1318                         } else {
1319                                 struct group *gr;
1320
1321                                 if (!(gr = getgrnam(optarg))) {
1322                                         fprintf(stderr, "bad user name \"%s\"\n", optarg);
1323                                         exit(EX_USAGE);
1324                                 }
1325                                 gid = gr->gr_gid;
1326                                 endpwent();
1327                         }
1328                         break;
1329                 case 'u':
1330                         got_user = 1;
1331                         user_name = optarg;
1332                         break;
1333                 case 'd':
1334                         domain_name = optarg; /* BB fix this - currently ignored */
1335                         got_domain = 1;
1336                         break;
1337                 case 'p':
1338                         if(mountpassword == NULL)
1339                                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1340                         if(mountpassword) {
1341                                 got_password = 1;
1342                                 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1343                         }
1344                         break;
1345                 case 'S':
1346                         get_password_from_file(0 /* stdin */,NULL);
1347                         break;
1348                 case 't':
1349                         break;
1350                 case 'f':
1351                         ++fakemnt;
1352                         break;
1353                 default:
1354                         fprintf(stderr, "unknown mount option %c\n",c);
1355                         mount_cifs_usage(stderr);
1356                 }
1357         }
1358
1359         if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1360                 mount_cifs_usage(stderr);
1361         }
1362
1363         /* make sure mountpoint is legit */
1364         rc = check_mountpoint(thisprogram, mountpoint);
1365         if (rc)
1366                 goto mount_exit;
1367
1368         /* sanity check for unprivileged mounts */
1369         if (getuid()) {
1370                 rc = check_fstab(thisprogram, mountpoint, dev_name,
1371                                  &orgoptions);
1372                 if (rc)
1373                         goto mount_exit;
1374
1375                 /* enable any default user mount flags */
1376                 flags |= CIFS_SETUID_FLAGS;
1377         }
1378
1379         if (getenv("PASSWD")) {
1380                 if(mountpassword == NULL)
1381                         mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1382                 if(mountpassword) {
1383                         strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1384                         got_password = 1;
1385                 }
1386         } else if (getenv("PASSWD_FD")) {
1387                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1388         } else if (getenv("PASSWD_FILE")) {
1389                 get_password_from_file(0, getenv("PASSWD_FILE"));
1390         }
1391
1392         if (orgoptions && parse_options(&orgoptions, &flags)) {
1393                 rc = EX_USAGE;
1394                 goto mount_exit;
1395         }
1396
1397         if (getuid()) {
1398 #if !CIFS_LEGACY_SETUID_CHECK
1399                 if (!(flags & (MS_USERS|MS_USER))) {
1400                         fprintf(stderr, "%s: permission denied\n", thisprogram);
1401                         rc = EX_USAGE;
1402                         goto mount_exit;
1403                 }
1404 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1405                 
1406                 if (geteuid()) {
1407                         fprintf(stderr, "%s: not installed setuid - \"user\" "
1408                                         "CIFS mounts not supported.",
1409                                         thisprogram);
1410                         rc = EX_FAIL;
1411                         goto mount_exit;
1412                 }
1413         }
1414
1415         flags &= ~(MS_USERS|MS_USER);
1416
1417         addrhead = addr = parse_server(&share_name);
1418         if((addrhead == NULL) && (got_ip == 0)) {
1419                 fprintf(stderr, "No ip address specified and hostname not found\n");
1420                 rc = EX_USAGE;
1421                 goto mount_exit;
1422         }
1423         
1424         /* BB save off path and pop after mount returns? */
1425         resolved_path = (char *)malloc(PATH_MAX+1);
1426         if(resolved_path) {
1427                 /* Note that if we can not canonicalize the name, we get
1428                 another chance to see if it is valid when we chdir to it */
1429                 if (realpath(mountpoint, resolved_path)) {
1430                         mountpoint = resolved_path; 
1431                 }
1432         }
1433         if(got_user == 0) {
1434                 /* Note that the password will not be retrieved from the
1435                    USER env variable (ie user%password form) as there is
1436                    already a PASSWD environment varaible */
1437                 if (getenv("USER"))
1438                         user_name = strdup(getenv("USER"));
1439                 if (user_name == NULL)
1440                         user_name = getusername();
1441                 got_user = 1;
1442         }
1443        
1444         if(got_password == 0) {
1445                 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1446                                                            no good replacement yet. */
1447                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1448                 if (!tmp_pass || !mountpassword) {
1449                         fprintf(stderr, "Password not entered, exiting\n");
1450                         exit(EX_USAGE);
1451                 }
1452                 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1453                 got_password = 1;
1454         }
1455         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1456            remember to clear parms and overwrite password field before launching */
1457         if(orgoptions) {
1458                 optlen = strlen(orgoptions);
1459                 orgoptlen = optlen;
1460         } else
1461                 optlen = 0;
1462         if(share_name)
1463                 optlen += strlen(share_name) + 4;
1464         else {
1465                 fprintf(stderr, "No server share name specified\n");
1466                 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1467                 exit(EX_USAGE);
1468         }
1469         if(user_name)
1470                 optlen += strlen(user_name) + 6;
1471         optlen += MAX_ADDRESS_LEN + 4;
1472         if(mountpassword)
1473                 optlen += strlen(mountpassword) + 6;
1474 mount_retry:
1475         SAFE_FREE(options);
1476         options_size = optlen + 10 + DOMAIN_SIZE;
1477         options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain=  , domain name itself was counted as part of the length username string above */);
1478
1479         if(options == NULL) {
1480                 fprintf(stderr, "Could not allocate memory for mount options\n");
1481                 exit(EX_SYSERR);
1482         }
1483
1484         strlcpy(options, "unc=", options_size);
1485         strlcat(options,share_name,options_size);
1486         /* scan backwards and reverse direction of slash */
1487         temp = strrchr(options, '/');
1488         if(temp > options + 6)
1489                 *temp = '\\';
1490         if(user_name) {
1491                 /* check for syntax like user=domain\user */
1492                 if(got_domain == 0)
1493                         domain_name = check_for_domain(&user_name);
1494                 strlcat(options,",user=",options_size);
1495                 strlcat(options,user_name,options_size);
1496         }
1497         if(retry == 0) {
1498                 if(domain_name) {
1499                         /* extra length accounted for in option string above */
1500                         strlcat(options,",domain=",options_size);
1501                         strlcat(options,domain_name,options_size);
1502                 }
1503         }
1504         if(mountpassword) {
1505                 /* Commas have to be doubled, or else they will
1506                 look like the parameter separator */
1507 /*              if(sep is not set)*/
1508                 if(retry == 0)
1509                         check_for_comma(&mountpassword);
1510                 strlcat(options,",pass=",options_size);
1511                 strlcat(options,mountpassword,options_size);
1512         }
1513
1514         strlcat(options,",ver=",options_size);
1515         strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1516
1517         if(orgoptions) {
1518                 strlcat(options,",",options_size);
1519                 strlcat(options,orgoptions,options_size);
1520         }
1521         if(prefixpath) {
1522                 strlcat(options,",prefixpath=",options_size);
1523                 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1524         }
1525         if(verboseflag)
1526                 fprintf(stderr, "\nmount.cifs kernel mount options %s \n",options);
1527
1528         /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1529         replace_char(dev_name, '\\', '/', strlen(share_name));
1530
1531         if (!got_ip && addr) {
1532                 strlcat(options, ",ip=", options_size);
1533                 current_len = strnlen(options, options_size);
1534                 optionstail = options + current_len;
1535                 switch (addr->ai_addr->sa_family) {
1536                 case AF_INET6:
1537                         addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1538                         ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1539                                            options_size - current_len);
1540                         break;
1541                 case AF_INET:
1542                         addr4 = (struct sockaddr_in *) addr->ai_addr;
1543                         ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1544                                            options_size - current_len);
1545                         break;
1546                 default:
1547                         ipaddr = NULL;
1548                 }
1549
1550                 /* if the address looks bogus, try the next one */
1551                 if (!ipaddr) {
1552                         addr = addr->ai_next;
1553                         if (addr)
1554                                 goto mount_retry;
1555                         rc = EX_SYSERR;
1556                         goto mount_exit;
1557                 }
1558         }
1559
1560         if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1561                 strlcat(options, "%", options_size);
1562                 current_len = strnlen(options, options_size);
1563                 optionstail = options + current_len;
1564                 snprintf(optionstail, options_size - current_len, "%u",
1565                          addr6->sin6_scope_id);
1566         }
1567
1568         if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1569                 switch (errno) {
1570                 case ECONNREFUSED:
1571                 case EHOSTUNREACH:
1572                         if (addr) {
1573                                 addr = addr->ai_next;
1574                                 if (addr)
1575                                         goto mount_retry;
1576                         }
1577                         break;
1578                 case ENODEV:
1579                         fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1580                         break;
1581                 case ENXIO:
1582                         if(retry == 0) {
1583                                 retry = 1;
1584                                 if (uppercase_string(dev_name) &&
1585                                     uppercase_string(share_name) &&
1586                                     uppercase_string(prefixpath)) {
1587                                         fprintf(stderr, "retrying with upper case share name\n");
1588                                         goto mount_retry;
1589                                 }
1590                         }
1591                 }
1592                 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1593                 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1594                        "mount.cifs)\n");
1595                 rc = EX_FAIL;
1596                 goto mount_exit;
1597         }
1598
1599         if (nomtab)
1600                 goto mount_exit;
1601         atexit(unlock_mtab);
1602         rc = lock_mtab();
1603         if (rc) {
1604                 fprintf(stderr, "cannot lock mtab");
1605                 goto mount_exit;
1606         }
1607         pmntfile = setmntent(MOUNTED, "a+");
1608         if (!pmntfile) {
1609                 fprintf(stderr, "could not update mount table\n");
1610                 unlock_mtab();
1611                 rc = EX_FILEIO;
1612                 goto mount_exit;
1613         }
1614         mountent.mnt_fsname = dev_name;
1615         mountent.mnt_dir = mountpoint;
1616         mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1617         mountent.mnt_opts = (char *)malloc(220);
1618         if(mountent.mnt_opts) {
1619                 char * mount_user = getusername();
1620                 memset(mountent.mnt_opts,0,200);
1621                 if(flags & MS_RDONLY)
1622                         strlcat(mountent.mnt_opts,"ro",220);
1623                 else
1624                         strlcat(mountent.mnt_opts,"rw",220);
1625                 if(flags & MS_MANDLOCK)
1626                         strlcat(mountent.mnt_opts,",mand",220);
1627                 if(flags & MS_NOEXEC)
1628                         strlcat(mountent.mnt_opts,",noexec",220);
1629                 if(flags & MS_NOSUID)
1630                         strlcat(mountent.mnt_opts,",nosuid",220);
1631                 if(flags & MS_NODEV)
1632                         strlcat(mountent.mnt_opts,",nodev",220);
1633                 if(flags & MS_SYNCHRONOUS)
1634                         strlcat(mountent.mnt_opts,",sync",220);
1635                 if(mount_user) {
1636                         if(getuid() != 0) {
1637                                 strlcat(mountent.mnt_opts,
1638                                         ",user=", 220);
1639                                 strlcat(mountent.mnt_opts,
1640                                         mount_user, 220);
1641                         }
1642                 }
1643         }
1644         mountent.mnt_freq = 0;
1645         mountent.mnt_passno = 0;
1646         rc = addmntent(pmntfile,&mountent);
1647         endmntent(pmntfile);
1648         unlock_mtab();
1649         SAFE_FREE(mountent.mnt_opts);
1650         if (rc)
1651                 rc = EX_FILEIO;
1652 mount_exit:
1653         if(mountpassword) {
1654                 int len = strlen(mountpassword);
1655                 memset(mountpassword,0,len);
1656                 SAFE_FREE(mountpassword);
1657         }
1658
1659         if (addrhead)
1660                 freeaddrinfo(addrhead);
1661         SAFE_FREE(options);
1662         SAFE_FREE(orgoptions);
1663         SAFE_FREE(resolved_path);
1664         SAFE_FREE(share_name);
1665         exit(rc);
1666 }