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