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