mount.cifs: check access of credential files before opening
[kai/samba.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
324         i = access(file_name, R_OK);
325         if (i)
326                 return i;
327
328         fs = fopen(file_name,"r");
329         if(fs == NULL)
330                 return errno;
331         line_buf = (char *)malloc(4096);
332         if(line_buf == NULL) {
333                 fclose(fs);
334                 return ENOMEM;
335         }
336
337         while(fgets(line_buf,4096,fs)) {
338                 /* parse line from credential file */
339
340                 /* eat leading white space */
341                 for(i=0;i<4086;i++) {
342                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
343                                 break;
344                         /* if whitespace - skip past it */
345                 }
346                 if (strncasecmp("username",line_buf+i,8) == 0) {
347                         temp_val = strchr(line_buf + i,'=');
348                         if(temp_val) {
349                                 /* go past equals sign */
350                                 temp_val++;
351                                 for(length = 0;length<4087;length++) {
352                                         if ((temp_val[length] == '\n')
353                                             || (temp_val[length] == '\0')) {
354                                                 temp_val[length] = '\0';
355                                                 break;
356                                         }
357                                 }
358                                 if(length > 4086) {
359                                         fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
360                                         memset(line_buf,0,4096);
361                                         exit(EX_USAGE);
362                                 } else {
363                                         got_user = 1;
364                                         user_name = (char *)calloc(1 + length,1);
365                                         /* BB adding free of user_name string before exit,
366                                                 not really necessary but would be cleaner */
367                                         strlcpy(user_name,temp_val, length+1);
368                                 }
369                         }
370                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
371                         temp_val = strchr(line_buf+i,'=');
372                         if(temp_val) {
373                                 /* go past equals sign */
374                                 temp_val++;
375                                 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
376                                         if ((temp_val[length] == '\n')
377                                             || (temp_val[length] == '\0')) {
378                                                 temp_val[length] = '\0';
379                                                 break;
380                                         }
381                                 }
382                                 if(length > MOUNT_PASSWD_SIZE) {
383                                         fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
384                                         memset(line_buf,0, 4096);
385                                         exit(EX_USAGE);
386                                 } else {
387                                         if(mountpassword == NULL) {
388                                                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
389                                         } else
390                                                 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
391                                         if(mountpassword) {
392                                                 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
393                                                 got_password = 1;
394                                         }
395                                 }
396                         }
397                 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
398                         temp_val = strchr(line_buf+i,'=');
399                         if(temp_val) {
400                                 /* go past equals sign */
401                                 temp_val++;
402                                 if(verboseflag)
403                                         fprintf(stderr, "\nDomain %s\n",temp_val);
404                                 for(length = 0;length<DOMAIN_SIZE+1;length++) {
405                                         if ((temp_val[length] == '\n')
406                                             || (temp_val[length] == '\0')) {
407                                                 temp_val[length] = '\0';
408                                                 break;
409                                         }
410                                 }
411                                 if(length > DOMAIN_SIZE) {
412                                         fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
413                                         exit(EX_USAGE);
414                                 } else {
415                                         if(domain_name == NULL) {
416                                                 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
417                                         } else
418                                                 memset(domain_name,0,DOMAIN_SIZE);
419                                         if(domain_name) {
420                                                 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
421                                                 got_domain = 1;
422                                         }
423                                 }
424                         }
425                 }
426
427         }
428         fclose(fs);
429         SAFE_FREE(line_buf);
430         return 0;
431 }
432
433 static int get_password_from_file(int file_descript, char * filename)
434 {
435         int rc = 0;
436         int i;
437         char c;
438
439         if(mountpassword == NULL)
440                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
441         else 
442                 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
443
444         if (mountpassword == NULL) {
445                 fprintf(stderr, "malloc failed\n");
446                 exit(EX_SYSERR);
447         }
448
449         if(filename != NULL) {
450                 rc = access(filename, R_OK);
451                 if (rc) {
452                         fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
453                                         filename, strerror(errno));
454                         exit(EX_SYSERR);
455                 }
456                 file_descript = open(filename, O_RDONLY);
457                 if(file_descript < 0) {
458                         fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
459                                    strerror(errno),filename);
460                         exit(EX_SYSERR);
461                 }
462         }
463         /* else file already open and fd provided */
464
465         for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
466                 rc = read(file_descript,&c,1);
467                 if(rc < 0) {
468                         fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
469                         if(filename != NULL)
470                                 close(file_descript);
471                         exit(EX_SYSERR);
472                 } else if(rc == 0) {
473                         if(mountpassword[0] == 0) {
474                                 if(verboseflag)
475                                         fprintf(stderr, "\nWarning: null password used since cifs password file empty");
476                         }
477                         break;
478                 } else /* read valid character */ {
479                         if((c == 0) || (c == '\n')) {
480                                 mountpassword[i] = '\0';
481                                 break;
482                         } else 
483                                 mountpassword[i] = c;
484                 }
485         }
486         if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
487                 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
488                         MOUNT_PASSWD_SIZE);
489         }
490         got_password = 1;
491         if(filename != NULL) {
492                 close(file_descript);
493         }
494
495         return rc;
496 }
497
498 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
499 {
500         const char * data;
501         char * percent_char = NULL;
502         char * value = NULL;
503         char * next_keyword = NULL;
504         char * out = NULL;
505         int out_len = 0;
506         int word_len;
507         int rc = 0;
508         char user[32];
509         char group[32];
510
511         if (!optionsp || !*optionsp)
512                 return 1;
513         data = *optionsp;
514
515         if(verboseflag)
516                 fprintf(stderr, "parsing options: %s\n", data);
517
518         /* BB fixme check for separator override BB */
519
520         if (getuid()) {
521                 got_uid = 1;
522                 snprintf(user,sizeof(user),"%u",getuid());
523                 got_gid = 1;
524                 snprintf(group,sizeof(group),"%u",getgid());
525         }
526
527 /* while ((data = strsep(&options, ",")) != NULL) { */
528         while(data != NULL) {
529                 /*  check if ends with trailing comma */
530                 if(*data == 0)
531                         break;
532
533                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
534                 /* data  = next keyword */
535                 /* value = next value ie stuff after equal sign */
536
537                 next_keyword = strchr(data,','); /* BB handle sep= */
538         
539                 /* temporarily null terminate end of keyword=value pair */
540                 if(next_keyword)
541                         *next_keyword++ = 0;
542
543                 /* temporarily null terminate keyword to make keyword and value distinct */
544                 if ((value = strchr(data, '=')) != NULL) {
545                         *value = '\0';
546                         value++;
547                 }
548
549                 if (strncmp(data, "users",5) == 0) {
550                         if(!value || !*value) {
551                                 *filesys_flags |= MS_USERS;
552                                 goto nocopy;
553                         }
554                 } else if (strncmp(data, "user_xattr",10) == 0) {
555                    /* do nothing - need to skip so not parsed as user name */
556                 } else if (strncmp(data, "user", 4) == 0) {
557
558                         if (!value || !*value) {
559                                 if(data[4] == '\0') {
560                                         *filesys_flags |= MS_USER;
561                                         goto nocopy;
562                                 } else {
563                                         fprintf(stderr, "username specified with no parameter\n");
564                                         SAFE_FREE(out);
565                                         return 1;       /* needs_arg; */
566                                 }
567                         } else {
568                                 if (strnlen(value, 260) < 260) {
569                                         got_user=1;
570                                         percent_char = strchr(value,'%');
571                                         if(percent_char) {
572                                                 *percent_char = ',';
573                                                 if(mountpassword == NULL)
574                                                         mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
575                                                 if(mountpassword) {
576                                                         if(got_password)
577                                                                 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
578                                                         got_password = 1;
579                                                         percent_char++;
580                                                         strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
581                                                 /*  remove password from username */
582                                                         while(*percent_char != 0) {
583                                                                 *percent_char = ',';
584                                                                 percent_char++;
585                                                         }
586                                                 }
587                                         }
588                                         /* this is only case in which the user
589                                         name buf is not malloc - so we have to
590                                         check for domain name embedded within
591                                         the user name here since the later
592                                         call to check_for_domain will not be
593                                         invoked */
594                                         domain_name = check_for_domain(&value);
595                                 } else {
596                                         fprintf(stderr, "username too long\n");
597                                         SAFE_FREE(out);
598                                         return 1;
599                                 }
600                         }
601                 } else if (strncmp(data, "pass", 4) == 0) {
602                         if (!value || !*value) {
603                                 if(got_password) {
604                                         fprintf(stderr, "\npassword specified twice, ignoring second\n");
605                                 } else
606                                         got_password = 1;
607                         } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
608                                 if(got_password)
609                                         fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
610                                 got_password = 1;
611                         } else {
612                                 fprintf(stderr, "password too long\n");
613                                 SAFE_FREE(out);
614                                 return 1;
615                         }
616                 } else if (strncmp(data, "sec", 3) == 0) {
617                         if (value) {
618                                 if (!strncmp(value, "none", 4) ||
619                                     !strncmp(value, "krb5", 4))
620                                         got_password = 1;
621                         }
622                 } else if (strncmp(data, "ip", 2) == 0) {
623                         if (!value || !*value) {
624                                 fprintf(stderr, "target ip address argument missing");
625                         } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
626                                 if(verboseflag)
627                                         fprintf(stderr, "ip address %s override specified\n",value);
628                                 got_ip = 1;
629                         } else {
630                                 fprintf(stderr, "ip address too long\n");
631                                 SAFE_FREE(out);
632                                 return 1;
633                         }
634                 } else if ((strncmp(data, "unc", 3) == 0)
635                    || (strncmp(data, "target", 6) == 0)
636                    || (strncmp(data, "path", 4) == 0)) {
637                         if (!value || !*value) {
638                                 fprintf(stderr, "invalid path to network resource\n");
639                                 SAFE_FREE(out);
640                                 return 1;  /* needs_arg; */
641                         } else if(strnlen(value,5) < 5) {
642                                 fprintf(stderr, "UNC name too short");
643                         }
644
645                         if (strnlen(value, 300) < 300) {
646                                 got_unc = 1;
647                                 if (strncmp(value, "//", 2) == 0) {
648                                         if(got_unc)
649                                                 fprintf(stderr, "unc name specified twice, ignoring second\n");
650                                         else
651                                                 got_unc = 1;
652                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
653                                         fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
654                                         SAFE_FREE(out);
655                                         return 1;
656                                 } else {
657                                         if(got_unc)
658                                                 fprintf(stderr, "unc name specified twice, ignoring second\n");
659                                         else
660                                                 got_unc = 1;
661                                 }
662                         } else {
663                                 fprintf(stderr, "CIFS: UNC name too long\n");
664                                 SAFE_FREE(out);
665                                 return 1;
666                         }
667                 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
668                            || (strncmp(data, "workg", 5) == 0)) {
669                         /* note this allows for synonyms of "domain"
670                            such as "DOM" and "dom" and "workgroup"
671                            and "WORKGRP" etc. */
672                         if (!value || !*value) {
673                                 fprintf(stderr, "CIFS: invalid domain name\n");
674                                 SAFE_FREE(out);
675                                 return 1;       /* needs_arg; */
676                         }
677                         if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
678                                 got_domain = 1;
679                         } else {
680                                 fprintf(stderr, "domain name too long\n");
681                                 SAFE_FREE(out);
682                                 return 1;
683                         }
684                 } else if (strncmp(data, "cred", 4) == 0) {
685                         if (value && *value) {
686                                 rc = open_cred_file(value);
687                                 if(rc) {
688                                         fprintf(stderr, "error %d (%s) opening credential file %s\n",
689                                                 rc, strerror(rc), value);
690                                         SAFE_FREE(out);
691                                         return 1;
692                                 }
693                         } else {
694                                 fprintf(stderr, "invalid credential file name specified\n");
695                                 SAFE_FREE(out);
696                                 return 1;
697                         }
698                 } else if (strncmp(data, "uid", 3) == 0) {
699                         if (value && *value) {
700                                 got_uid = 1;
701                                 if (!isdigit(*value)) {
702                                         struct passwd *pw;
703
704                                         if (!(pw = getpwnam(value))) {
705                                                 fprintf(stderr, "bad user name \"%s\"\n", value);
706                                                 exit(EX_USAGE);
707                                         }
708                                         snprintf(user, sizeof(user), "%u", pw->pw_uid);
709                                 } else {
710                                         strlcpy(user,value,sizeof(user));
711                                 }
712                         }
713                         goto nocopy;
714                 } else if (strncmp(data, "gid", 3) == 0) {
715                         if (value && *value) {
716                                 got_gid = 1;
717                                 if (!isdigit(*value)) {
718                                         struct group *gr;
719
720                                         if (!(gr = getgrnam(value))) {
721                                                 fprintf(stderr, "bad group name \"%s\"\n", value);
722                                                 exit(EX_USAGE);
723                                         }
724                                         snprintf(group, sizeof(group), "%u", gr->gr_gid);
725                                 } else {
726                                         strlcpy(group,value,sizeof(group));
727                                 }
728                         }
729                         goto nocopy;
730        /* fmask and dmask synonyms for people used to smbfs syntax */
731                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
732                         if (!value || !*value) {
733                                 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
734                                 SAFE_FREE(out);
735                                 return 1;
736                         }
737
738                         if (value[0] != '0') {
739                                 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
740                         }
741
742                         if (strcmp (data, "fmask") == 0) {
743                                 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
744                                 data = "file_mode"; /* BB fix this */
745                         }
746                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
747                         if (!value || !*value) {
748                                 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
749                                 SAFE_FREE(out);
750                                 return 1;
751                         }
752
753                         if (value[0] != '0') {
754                                 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
755                         }
756
757                         if (strcmp (data, "dmask") == 0) {
758                                 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
759                                 data = "dir_mode";
760                         }
761                         /* the following eight mount options should be
762                         stripped out from what is passed into the kernel
763                         since these eight options are best passed as the
764                         mount flags rather than redundantly to the kernel 
765                         and could generate spurious warnings depending on the
766                         level of the corresponding cifs vfs kernel code */
767                 } else if (strncmp(data, "nosuid", 6) == 0) {
768                         *filesys_flags |= MS_NOSUID;
769                 } else if (strncmp(data, "suid", 4) == 0) {
770                         *filesys_flags &= ~MS_NOSUID;
771                 } else if (strncmp(data, "nodev", 5) == 0) {
772                         *filesys_flags |= MS_NODEV;
773                 } else if ((strncmp(data, "nobrl", 5) == 0) || 
774                            (strncmp(data, "nolock", 6) == 0)) {
775                         *filesys_flags &= ~MS_MANDLOCK;
776                 } else if (strncmp(data, "dev", 3) == 0) {
777                         *filesys_flags &= ~MS_NODEV;
778                 } else if (strncmp(data, "noexec", 6) == 0) {
779                         *filesys_flags |= MS_NOEXEC;
780                 } else if (strncmp(data, "exec", 4) == 0) {
781                         *filesys_flags &= ~MS_NOEXEC;
782                 } else if (strncmp(data, "guest", 5) == 0) {
783                         user_name = (char *)calloc(1, 1);
784                         got_user = 1;
785                         got_password = 1;
786                 } else if (strncmp(data, "ro", 2) == 0) {
787                         *filesys_flags |= MS_RDONLY;
788                         goto nocopy;
789                 } else if (strncmp(data, "rw", 2) == 0) {
790                         *filesys_flags &= ~MS_RDONLY;
791                         goto nocopy;
792                 } else if (strncmp(data, "remount", 7) == 0) {
793                         *filesys_flags |= MS_REMOUNT;
794                 } /* else if (strnicmp(data, "port", 4) == 0) {
795                         if (value && *value) {
796                                 vol->port =
797                                         simple_strtoul(value, &value, 0);
798                         }
799                 } else if (strnicmp(data, "rsize", 5) == 0) {
800                         if (value && *value) {
801                                 vol->rsize =
802                                         simple_strtoul(value, &value, 0);
803                         }
804                 } else if (strnicmp(data, "wsize", 5) == 0) {
805                         if (value && *value) {
806                                 vol->wsize =
807                                         simple_strtoul(value, &value, 0);
808                         }
809                 } else if (strnicmp(data, "version", 3) == 0) {
810                 } else {
811                         fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
812                 } */ /* nothing to do on those four mount options above.
813                         Just pass to kernel and ignore them here */
814
815                 /* Copy (possibly modified) option to out */
816                 word_len = strlen(data);
817                 if (value)
818                         word_len += 1 + strlen(value);
819
820                 out = (char *)realloc(out, out_len + word_len + 2);
821                 if (out == NULL) {
822                         perror("malloc");
823                         exit(EX_SYSERR);
824                 }
825
826                 if (out_len) {
827                         strlcat(out, ",", out_len + word_len + 2);
828                         out_len++;
829                 }
830
831                 if (value)
832                         snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
833                 else
834                         snprintf(out + out_len, word_len + 1, "%s", data);
835                 out_len = strlen(out);
836
837 nocopy:
838                 data = next_keyword;
839         }
840
841         /* special-case the uid and gid */
842         if (got_uid) {
843                 word_len = strlen(user);
844
845                 out = (char *)realloc(out, out_len + word_len + 6);
846                 if (out == NULL) {
847                         perror("malloc");
848                         exit(EX_SYSERR);
849                 }
850
851                 if (out_len) {
852                         strlcat(out, ",", out_len + word_len + 6);
853                         out_len++;
854                 }
855                 snprintf(out + out_len, word_len + 5, "uid=%s", user);
856                 out_len = strlen(out);
857         }
858         if (got_gid) {
859                 word_len = strlen(group);
860
861                 out = (char *)realloc(out, out_len + 1 + word_len + 6);
862                 if (out == NULL) {
863                 perror("malloc");
864                         exit(EX_SYSERR);
865                 }
866
867                 if (out_len) {
868                         strlcat(out, ",", out_len + word_len + 6);
869                         out_len++;
870                 }
871                 snprintf(out + out_len, word_len + 5, "gid=%s", group);
872                 out_len = strlen(out);
873         }
874
875         SAFE_FREE(*optionsp);
876         *optionsp = out;
877         return 0;
878 }
879
880 /* replace all (one or more) commas with double commas */
881 static void check_for_comma(char ** ppasswrd)
882 {
883         char *new_pass_buf;
884         char *pass;
885         int i,j;
886         int number_of_commas = 0;
887         int len;
888
889         if(ppasswrd == NULL)
890                 return;
891         else 
892                 (pass = *ppasswrd);
893
894         len = strlen(pass);
895
896         for(i=0;i<len;i++)  {
897                 if(pass[i] == ',')
898                         number_of_commas++;
899         }
900
901         if(number_of_commas == 0)
902                 return;
903         if(number_of_commas > MOUNT_PASSWD_SIZE) {
904                 /* would otherwise overflow the mount options buffer */
905                 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
906                 return;
907         }
908
909         new_pass_buf = (char *)malloc(len+number_of_commas+1);
910         if(new_pass_buf == NULL)
911                 return;
912
913         for(i=0,j=0;i<len;i++,j++) {
914                 new_pass_buf[j] = pass[i];
915                 if(pass[i] == ',') {
916                         j++;
917                         new_pass_buf[j] = pass[i];
918                 }
919         }
920         new_pass_buf[len+number_of_commas] = 0;
921
922         SAFE_FREE(*ppasswrd);
923         *ppasswrd = new_pass_buf;
924         
925         return;
926 }
927
928 /* Usernames can not have backslash in them and we use
929    [BB check if usernames can have forward slash in them BB] 
930    backslash as domain\user separator character
931 */
932 static char * check_for_domain(char **ppuser)
933 {
934         char * original_string;
935         char * usernm;
936         char * domainnm;
937         int    original_len;
938         int    len;
939         int    i;
940
941         if(ppuser == NULL)
942                 return NULL;
943
944         original_string = *ppuser;
945
946         if (original_string == NULL)
947                 return NULL;
948         
949         original_len = strlen(original_string);
950
951         usernm = strchr(*ppuser,'/');
952         if (usernm == NULL) {
953                 usernm = strchr(*ppuser,'\\');
954                 if (usernm == NULL)
955                         return NULL;
956         }
957
958         if(got_domain) {
959                 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
960                 return NULL;
961         }
962
963         usernm[0] = 0;
964         domainnm = *ppuser;
965         if (domainnm[0] != 0) {
966                 got_domain = 1;
967         } else {
968                 fprintf(stderr, "null domain\n");
969         }
970         len = strlen(domainnm);
971         /* reset domainm to new buffer, and copy
972         domain name into it */
973         domainnm = (char *)malloc(len+1);
974         if(domainnm == NULL)
975                 return NULL;
976
977         strlcpy(domainnm,*ppuser,len+1);
978
979 /*      move_string(*ppuser, usernm+1) */
980         len = strlen(usernm+1);
981
982         if(len >= original_len) {
983                 /* should not happen */
984                 return domainnm;
985         }
986
987         for(i=0;i<original_len;i++) {
988                 if(i<len)
989                         original_string[i] = usernm[i+1];
990                 else /* stuff with commas to remove last parm */
991                         original_string[i] = ',';
992         }
993
994         /* BB add check for more than one slash? 
995           strchr(*ppuser,'/');
996           strchr(*ppuser,'\\') 
997         */
998         
999         return domainnm;
1000 }
1001
1002 /* replace all occurances of "from" in a string with "to" */
1003 static void replace_char(char *string, char from, char to, int maxlen)
1004 {
1005         char *lastchar = string + maxlen;
1006         while (string) {
1007                 string = strchr(string, from);
1008                 if (string) {
1009                         *string = to;
1010                         if (string >= lastchar)
1011                                 return;
1012                 }
1013         }
1014 }
1015
1016 /* Note that caller frees the returned buffer if necessary */
1017 static struct addrinfo *
1018 parse_server(char ** punc_name)
1019 {
1020         char * unc_name = *punc_name;
1021         int length = strnlen(unc_name, MAX_UNC_LEN);
1022         char * share;
1023         struct addrinfo *addrlist;
1024         int rc;
1025
1026         if(length > (MAX_UNC_LEN - 1)) {
1027                 fprintf(stderr, "mount error: UNC name too long");
1028                 return NULL;
1029         }
1030         if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1031             (strncasecmp("smb://", unc_name, 6) == 0)) {
1032                 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1033                 return NULL;
1034         }
1035
1036         if(length < 3) {
1037                 /* BB add code to find DFS root here */
1038                 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1039                 return NULL;
1040         } else {
1041                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1042                         /* check for nfs syntax ie server:share */
1043                         share = strchr(unc_name,':');
1044                         if(share) {
1045                                 *punc_name = (char *)malloc(length+3);
1046                                 if(*punc_name == NULL) {
1047                                         /* put the original string back  if 
1048                                            no memory left */
1049                                         *punc_name = unc_name;
1050                                         return NULL;
1051                                 }
1052                                 *share = '/';
1053                                 strlcpy((*punc_name)+2,unc_name,length+1);
1054                                 SAFE_FREE(unc_name);
1055                                 unc_name = *punc_name;
1056                                 unc_name[length+2] = 0;
1057                                 goto continue_unc_parsing;
1058                         } else {
1059                                 fprintf(stderr, "mount error: improperly formatted UNC name.");
1060                                 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1061                                 return NULL;
1062                         }
1063                 } else {
1064 continue_unc_parsing:
1065                         unc_name[0] = '/';
1066                         unc_name[1] = '/';
1067                         unc_name += 2;
1068
1069                         /* allow for either delimiter between host and sharename */
1070                         if ((share = strpbrk(unc_name, "/\\"))) {
1071                                 *share = 0;  /* temporarily terminate the string */
1072                                 share += 1;
1073                                 if(got_ip == 0) {
1074                                         rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1075                                         if (rc != 0) {
1076                                                 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1077                                                         unc_name, gai_strerror(rc));
1078                                                 addrlist = NULL;
1079                                         }
1080                                 }
1081                                 *(share - 1) = '/'; /* put delimiter back */
1082
1083                                 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1084                                 if ((prefixpath = strpbrk(share, "/\\"))) {
1085                                         *prefixpath = 0;  /* permanently terminate the string */
1086                                         if (!strlen(++prefixpath))
1087                                                 prefixpath = NULL; /* this needs to be done explicitly */
1088                                 }
1089                                 if(got_ip) {
1090                                         if(verboseflag)
1091                                                 fprintf(stderr, "ip address specified explicitly\n");
1092                                         return NULL;
1093                                 }
1094                                 /* BB should we pass an alternate version of the share name as Unicode */
1095
1096                                 return addrlist; 
1097                         } else {
1098                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1099                                 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1100                                 return NULL;
1101                         }
1102                 }
1103         }
1104 }
1105
1106 static struct option longopts[] = {
1107         { "all", 0, NULL, 'a' },
1108         { "help",0, NULL, 'h' },
1109         { "move",0, NULL, 'm' },
1110         { "bind",0, NULL, 'b' },
1111         { "read-only", 0, NULL, 'r' },
1112         { "ro", 0, NULL, 'r' },
1113         { "verbose", 0, NULL, 'v' },
1114         { "version", 0, NULL, 'V' },
1115         { "read-write", 0, NULL, 'w' },
1116         { "rw", 0, NULL, 'w' },
1117         { "options", 1, NULL, 'o' },
1118         { "type", 1, NULL, 't' },
1119         { "rsize",1, NULL, 'R' },
1120         { "wsize",1, NULL, 'W' },
1121         { "uid", 1, NULL, '1'},
1122         { "gid", 1, NULL, '2'},
1123         { "user",1,NULL,'u'},
1124         { "username",1,NULL,'u'},
1125         { "dom",1,NULL,'d'},
1126         { "domain",1,NULL,'d'},
1127         { "password",1,NULL,'p'},
1128         { "pass",1,NULL,'p'},
1129         { "credentials",1,NULL,'c'},
1130         { "port",1,NULL,'P'},
1131         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1132         { NULL, 0, NULL, 0 }
1133 };
1134
1135 /* convert a string to uppercase. return false if the string
1136  * wasn't ASCII. Return success on a NULL ptr */
1137 static int
1138 uppercase_string(char *string)
1139 {
1140         if (!string)
1141                 return 1;
1142
1143         while (*string) {
1144                 /* check for unicode */
1145                 if ((unsigned char) string[0] & 0x80)
1146                         return 0;
1147                 *string = toupper((unsigned char) *string);
1148                 string++;
1149         }
1150
1151         return 1;
1152 }
1153
1154 static void print_cifs_mount_version(void)
1155 {
1156         printf("mount.cifs version: %s.%s%s\n",
1157                 MOUNT_CIFS_VERSION_MAJOR,
1158                 MOUNT_CIFS_VERSION_MINOR,
1159                 MOUNT_CIFS_VENDOR_SUFFIX);
1160 }
1161
1162 int main(int argc, char ** argv)
1163 {
1164         int c;
1165         unsigned long flags = MS_MANDLOCK;
1166         char * orgoptions = NULL;
1167         char * share_name = NULL;
1168         const char * ipaddr = NULL;
1169         char * uuid = NULL;
1170         char * mountpoint = NULL;
1171         char * options = NULL;
1172         char * optionstail;
1173         char * resolved_path = NULL;
1174         char * temp;
1175         char * dev_name;
1176         int rc = 0;
1177         int rsize = 0;
1178         int wsize = 0;
1179         int nomtab = 0;
1180         int uid = 0;
1181         int gid = 0;
1182         int optlen = 0;
1183         int orgoptlen = 0;
1184         size_t options_size = 0;
1185         size_t current_len;
1186         int retry = 0; /* set when we have to retry mount with uppercase */
1187         struct addrinfo *addrhead = NULL, *addr;
1188         struct utsname sysinfo;
1189         struct mntent mountent;
1190         struct sockaddr_in *addr4;
1191         struct sockaddr_in6 *addr6;
1192         FILE * pmntfile;
1193
1194         /* setlocale(LC_ALL, "");
1195         bindtextdomain(PACKAGE, LOCALEDIR);
1196         textdomain(PACKAGE); */
1197
1198         if(argc && argv)
1199                 thisprogram = argv[0];
1200         else
1201                 mount_cifs_usage(stderr);
1202
1203         if(thisprogram == NULL)
1204                 thisprogram = "mount.cifs";
1205
1206         uname(&sysinfo);
1207         /* BB add workstation name and domain and pass down */
1208
1209 /* #ifdef _GNU_SOURCE
1210         fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1211 #endif */
1212         if(argc > 2) {
1213                 dev_name = argv[1];
1214                 share_name = strndup(argv[1], MAX_UNC_LEN);
1215                 if (share_name == NULL) {
1216                         fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1217                         exit(EX_SYSERR);
1218                 }
1219                 mountpoint = argv[2];
1220         } else if (argc == 2) {
1221                 if ((strcmp(argv[1], "-V") == 0) ||
1222                     (strcmp(argv[1], "--version") == 0))
1223                 {
1224                         print_cifs_mount_version();
1225                         exit(0);
1226                 }
1227
1228                 if ((strcmp(argv[1], "-h") == 0) ||
1229                     (strcmp(argv[1], "-?") == 0) ||
1230                     (strcmp(argv[1], "--help") == 0))
1231                         mount_cifs_usage(stdout);
1232
1233                 mount_cifs_usage(stderr);
1234         } else {
1235                 mount_cifs_usage(stderr);
1236         }
1237
1238
1239         /* add sharename in opts string as unc= parm */
1240         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1241                          longopts, NULL)) != -1) {
1242                 switch (c) {
1243 /* No code to do the following  options yet */
1244 /*      case 'l':
1245                 list_with_volumelabel = 1;
1246                 break;
1247         case 'L':
1248                 volumelabel = optarg;
1249                 break; */
1250 /*      case 'a':              
1251                 ++mount_all;
1252                 break; */
1253
1254                 case '?':
1255                 case 'h':        /* help */
1256                         mount_cifs_usage(stdout);
1257                 case 'n':
1258                         ++nomtab;
1259                         break;
1260                 case 'b':
1261 #ifdef MS_BIND
1262                         flags |= MS_BIND;
1263 #else
1264                         fprintf(stderr,
1265                                 "option 'b' (MS_BIND) not supported\n");
1266 #endif
1267                         break;
1268                 case 'm':
1269 #ifdef MS_MOVE                
1270                         flags |= MS_MOVE;
1271 #else
1272                         fprintf(stderr,
1273                                 "option 'm' (MS_MOVE) not supported\n");
1274 #endif
1275                         break;
1276                 case 'o':
1277                         orgoptions = strdup(optarg);
1278                     break;
1279                 case 'r':  /* mount readonly */
1280                         flags |= MS_RDONLY;
1281                         break;
1282                 case 'U':
1283                         uuid = optarg;
1284                         break;
1285                 case 'v':
1286                         ++verboseflag;
1287                         break;
1288                 case 'V':
1289                         print_cifs_mount_version();
1290                         exit (0);
1291                 case 'w':
1292                         flags &= ~MS_RDONLY;
1293                         break;
1294                 case 'R':
1295                         rsize = atoi(optarg) ;
1296                         break;
1297                 case 'W':
1298                         wsize = atoi(optarg);
1299                         break;
1300                 case '1':
1301                         if (isdigit(*optarg)) {
1302                                 char *ep;
1303
1304                                 uid = strtoul(optarg, &ep, 10);
1305                                 if (*ep) {
1306                                         fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1307                                         exit(EX_USAGE);
1308                                 }
1309                         } else {
1310                                 struct passwd *pw;
1311
1312                                 if (!(pw = getpwnam(optarg))) {
1313                                         fprintf(stderr, "bad user name \"%s\"\n", optarg);
1314                                         exit(EX_USAGE);
1315                                 }
1316                                 uid = pw->pw_uid;
1317                                 endpwent();
1318                         }
1319                         break;
1320                 case '2':
1321                         if (isdigit(*optarg)) {
1322                                 char *ep;
1323
1324                                 gid = strtoul(optarg, &ep, 10);
1325                                 if (*ep) {
1326                                         fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1327                                         exit(EX_USAGE);
1328                                 }
1329                         } else {
1330                                 struct group *gr;
1331
1332                                 if (!(gr = getgrnam(optarg))) {
1333                                         fprintf(stderr, "bad user name \"%s\"\n", optarg);
1334                                         exit(EX_USAGE);
1335                                 }
1336                                 gid = gr->gr_gid;
1337                                 endpwent();
1338                         }
1339                         break;
1340                 case 'u':
1341                         got_user = 1;
1342                         user_name = optarg;
1343                         break;
1344                 case 'd':
1345                         domain_name = optarg; /* BB fix this - currently ignored */
1346                         got_domain = 1;
1347                         break;
1348                 case 'p':
1349                         if(mountpassword == NULL)
1350                                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1351                         if(mountpassword) {
1352                                 got_password = 1;
1353                                 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1354                         }
1355                         break;
1356                 case 'S':
1357                         get_password_from_file(0 /* stdin */,NULL);
1358                         break;
1359                 case 't':
1360                         break;
1361                 case 'f':
1362                         ++fakemnt;
1363                         break;
1364                 default:
1365                         fprintf(stderr, "unknown mount option %c\n",c);
1366                         mount_cifs_usage(stderr);
1367                 }
1368         }
1369
1370         if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1371                 mount_cifs_usage(stderr);
1372         }
1373
1374         /* make sure mountpoint is legit */
1375         rc = check_mountpoint(thisprogram, mountpoint);
1376         if (rc)
1377                 goto mount_exit;
1378
1379         /* sanity check for unprivileged mounts */
1380         if (getuid()) {
1381                 rc = check_fstab(thisprogram, mountpoint, dev_name,
1382                                  &orgoptions);
1383                 if (rc)
1384                         goto mount_exit;
1385
1386                 /* enable any default user mount flags */
1387                 flags |= CIFS_SETUID_FLAGS;
1388         }
1389
1390         if (getenv("PASSWD")) {
1391                 if(mountpassword == NULL)
1392                         mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1393                 if(mountpassword) {
1394                         strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1395                         got_password = 1;
1396                 }
1397         } else if (getenv("PASSWD_FD")) {
1398                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1399         } else if (getenv("PASSWD_FILE")) {
1400                 get_password_from_file(0, getenv("PASSWD_FILE"));
1401         }
1402
1403         if (orgoptions && parse_options(&orgoptions, &flags)) {
1404                 rc = EX_USAGE;
1405                 goto mount_exit;
1406         }
1407
1408         if (getuid()) {
1409 #if !CIFS_LEGACY_SETUID_CHECK
1410                 if (!(flags & (MS_USERS|MS_USER))) {
1411                         fprintf(stderr, "%s: permission denied\n", thisprogram);
1412                         rc = EX_USAGE;
1413                         goto mount_exit;
1414                 }
1415 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1416                 
1417                 if (geteuid()) {
1418                         fprintf(stderr, "%s: not installed setuid - \"user\" "
1419                                         "CIFS mounts not supported.",
1420                                         thisprogram);
1421                         rc = EX_FAIL;
1422                         goto mount_exit;
1423                 }
1424         }
1425
1426         flags &= ~(MS_USERS|MS_USER);
1427
1428         addrhead = addr = parse_server(&share_name);
1429         if((addrhead == NULL) && (got_ip == 0)) {
1430                 fprintf(stderr, "No ip address specified and hostname not found\n");
1431                 rc = EX_USAGE;
1432                 goto mount_exit;
1433         }
1434         
1435         /* BB save off path and pop after mount returns? */
1436         resolved_path = (char *)malloc(PATH_MAX+1);
1437         if(resolved_path) {
1438                 /* Note that if we can not canonicalize the name, we get
1439                 another chance to see if it is valid when we chdir to it */
1440                 if (realpath(mountpoint, resolved_path)) {
1441                         mountpoint = resolved_path; 
1442                 }
1443         }
1444         if(got_user == 0) {
1445                 /* Note that the password will not be retrieved from the
1446                    USER env variable (ie user%password form) as there is
1447                    already a PASSWD environment varaible */
1448                 if (getenv("USER"))
1449                         user_name = strdup(getenv("USER"));
1450                 if (user_name == NULL)
1451                         user_name = getusername();
1452                 got_user = 1;
1453         }
1454        
1455         if(got_password == 0) {
1456                 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1457                                                            no good replacement yet. */
1458                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1459                 if (!tmp_pass || !mountpassword) {
1460                         fprintf(stderr, "Password not entered, exiting\n");
1461                         exit(EX_USAGE);
1462                 }
1463                 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1464                 got_password = 1;
1465         }
1466         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1467            remember to clear parms and overwrite password field before launching */
1468         if(orgoptions) {
1469                 optlen = strlen(orgoptions);
1470                 orgoptlen = optlen;
1471         } else
1472                 optlen = 0;
1473         if(share_name)
1474                 optlen += strlen(share_name) + 4;
1475         else {
1476                 fprintf(stderr, "No server share name specified\n");
1477                 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1478                 exit(EX_USAGE);
1479         }
1480         if(user_name)
1481                 optlen += strlen(user_name) + 6;
1482         optlen += MAX_ADDRESS_LEN + 4;
1483         if(mountpassword)
1484                 optlen += strlen(mountpassword) + 6;
1485 mount_retry:
1486         SAFE_FREE(options);
1487         options_size = optlen + 10 + DOMAIN_SIZE;
1488         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 */);
1489
1490         if(options == NULL) {
1491                 fprintf(stderr, "Could not allocate memory for mount options\n");
1492                 exit(EX_SYSERR);
1493         }
1494
1495         strlcpy(options, "unc=", options_size);
1496         strlcat(options,share_name,options_size);
1497         /* scan backwards and reverse direction of slash */
1498         temp = strrchr(options, '/');
1499         if(temp > options + 6)
1500                 *temp = '\\';
1501         if(user_name) {
1502                 /* check for syntax like user=domain\user */
1503                 if(got_domain == 0)
1504                         domain_name = check_for_domain(&user_name);
1505                 strlcat(options,",user=",options_size);
1506                 strlcat(options,user_name,options_size);
1507         }
1508         if(retry == 0) {
1509                 if(domain_name) {
1510                         /* extra length accounted for in option string above */
1511                         strlcat(options,",domain=",options_size);
1512                         strlcat(options,domain_name,options_size);
1513                 }
1514         }
1515         if(mountpassword) {
1516                 /* Commas have to be doubled, or else they will
1517                 look like the parameter separator */
1518 /*              if(sep is not set)*/
1519                 if(retry == 0)
1520                         check_for_comma(&mountpassword);
1521                 strlcat(options,",pass=",options_size);
1522                 strlcat(options,mountpassword,options_size);
1523         }
1524
1525         strlcat(options,",ver=",options_size);
1526         strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1527
1528         if(orgoptions) {
1529                 strlcat(options,",",options_size);
1530                 strlcat(options,orgoptions,options_size);
1531         }
1532         if(prefixpath) {
1533                 strlcat(options,",prefixpath=",options_size);
1534                 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1535         }
1536         if(verboseflag)
1537                 fprintf(stderr, "\nmount.cifs kernel mount options %s \n",options);
1538
1539         /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1540         replace_char(dev_name, '\\', '/', strlen(share_name));
1541
1542         if (!got_ip && addr) {
1543                 strlcat(options, ",ip=", options_size);
1544                 current_len = strnlen(options, options_size);
1545                 optionstail = options + current_len;
1546                 switch (addr->ai_addr->sa_family) {
1547                 case AF_INET6:
1548                         addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1549                         ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1550                                            options_size - current_len);
1551                         break;
1552                 case AF_INET:
1553                         addr4 = (struct sockaddr_in *) addr->ai_addr;
1554                         ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1555                                            options_size - current_len);
1556                         break;
1557                 default:
1558                         ipaddr = NULL;
1559                 }
1560
1561                 /* if the address looks bogus, try the next one */
1562                 if (!ipaddr) {
1563                         addr = addr->ai_next;
1564                         if (addr)
1565                                 goto mount_retry;
1566                         rc = EX_SYSERR;
1567                         goto mount_exit;
1568                 }
1569         }
1570
1571         if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1572                 strlcat(options, "%", options_size);
1573                 current_len = strnlen(options, options_size);
1574                 optionstail = options + current_len;
1575                 snprintf(optionstail, options_size - current_len, "%u",
1576                          addr6->sin6_scope_id);
1577         }
1578
1579         if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1580                 switch (errno) {
1581                 case ECONNREFUSED:
1582                 case EHOSTUNREACH:
1583                         if (addr) {
1584                                 addr = addr->ai_next;
1585                                 if (addr)
1586                                         goto mount_retry;
1587                         }
1588                         break;
1589                 case ENODEV:
1590                         fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1591                         break;
1592                 case ENXIO:
1593                         if(retry == 0) {
1594                                 retry = 1;
1595                                 if (uppercase_string(dev_name) &&
1596                                     uppercase_string(share_name) &&
1597                                     uppercase_string(prefixpath)) {
1598                                         fprintf(stderr, "retrying with upper case share name\n");
1599                                         goto mount_retry;
1600                                 }
1601                         }
1602                 }
1603                 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1604                 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1605                        "mount.cifs)\n");
1606                 rc = EX_FAIL;
1607                 goto mount_exit;
1608         }
1609
1610         if (nomtab)
1611                 goto mount_exit;
1612         atexit(unlock_mtab);
1613         rc = lock_mtab();
1614         if (rc) {
1615                 fprintf(stderr, "cannot lock mtab");
1616                 goto mount_exit;
1617         }
1618         pmntfile = setmntent(MOUNTED, "a+");
1619         if (!pmntfile) {
1620                 fprintf(stderr, "could not update mount table\n");
1621                 unlock_mtab();
1622                 rc = EX_FILEIO;
1623                 goto mount_exit;
1624         }
1625         mountent.mnt_fsname = dev_name;
1626         mountent.mnt_dir = mountpoint;
1627         mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1628         mountent.mnt_opts = (char *)malloc(220);
1629         if(mountent.mnt_opts) {
1630                 char * mount_user = getusername();
1631                 memset(mountent.mnt_opts,0,200);
1632                 if(flags & MS_RDONLY)
1633                         strlcat(mountent.mnt_opts,"ro",220);
1634                 else
1635                         strlcat(mountent.mnt_opts,"rw",220);
1636                 if(flags & MS_MANDLOCK)
1637                         strlcat(mountent.mnt_opts,",mand",220);
1638                 if(flags & MS_NOEXEC)
1639                         strlcat(mountent.mnt_opts,",noexec",220);
1640                 if(flags & MS_NOSUID)
1641                         strlcat(mountent.mnt_opts,",nosuid",220);
1642                 if(flags & MS_NODEV)
1643                         strlcat(mountent.mnt_opts,",nodev",220);
1644                 if(flags & MS_SYNCHRONOUS)
1645                         strlcat(mountent.mnt_opts,",sync",220);
1646                 if(mount_user) {
1647                         if(getuid() != 0) {
1648                                 strlcat(mountent.mnt_opts,
1649                                         ",user=", 220);
1650                                 strlcat(mountent.mnt_opts,
1651                                         mount_user, 220);
1652                         }
1653                 }
1654         }
1655         mountent.mnt_freq = 0;
1656         mountent.mnt_passno = 0;
1657         rc = addmntent(pmntfile,&mountent);
1658         endmntent(pmntfile);
1659         unlock_mtab();
1660         SAFE_FREE(mountent.mnt_opts);
1661         if (rc)
1662                 rc = EX_FILEIO;
1663 mount_exit:
1664         if(mountpassword) {
1665                 int len = strlen(mountpassword);
1666                 memset(mountpassword,0,len);
1667                 SAFE_FREE(mountpassword);
1668         }
1669
1670         if (addrhead)
1671                 freeaddrinfo(addrhead);
1672         SAFE_FREE(options);
1673         SAFE_FREE(orgoptions);
1674         SAFE_FREE(resolved_path);
1675         SAFE_FREE(share_name);
1676         exit(rc);
1677 }