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