s4:upgrade script - rename it to "upgrade_from_s3" and do some rework
[ira/wip.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(mountpoint, &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 = check_mountpoint(thisprogram, mountpoint);
1381         if (rc)
1382                 goto mount_exit;
1383
1384         /* sanity check for unprivileged mounts */
1385         if (getuid()) {
1386                 rc = check_fstab(thisprogram, mountpoint, dev_name,
1387                                  &orgoptions);
1388                 if (rc)
1389                         goto mount_exit;
1390
1391                 /* enable any default user mount flags */
1392                 flags |= CIFS_SETUID_FLAGS;
1393         }
1394
1395         if (getenv("PASSWD")) {
1396                 if(mountpassword == NULL)
1397                         mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1398                 if(mountpassword) {
1399                         strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1400                         got_password = 1;
1401                 }
1402         } else if (getenv("PASSWD_FD")) {
1403                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1404         } else if (getenv("PASSWD_FILE")) {
1405                 get_password_from_file(0, getenv("PASSWD_FILE"));
1406         }
1407
1408         if (orgoptions && parse_options(&orgoptions, &flags)) {
1409                 rc = EX_USAGE;
1410                 goto mount_exit;
1411         }
1412
1413         if (getuid()) {
1414 #if !CIFS_LEGACY_SETUID_CHECK
1415                 if (!(flags & (MS_USERS|MS_USER))) {
1416                         fprintf(stderr, "%s: permission denied\n", thisprogram);
1417                         rc = EX_USAGE;
1418                         goto mount_exit;
1419                 }
1420 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1421                 
1422                 if (geteuid()) {
1423                         fprintf(stderr, "%s: not installed setuid - \"user\" "
1424                                         "CIFS mounts not supported.",
1425                                         thisprogram);
1426                         rc = EX_FAIL;
1427                         goto mount_exit;
1428                 }
1429         }
1430
1431         flags &= ~(MS_USERS|MS_USER);
1432
1433         addrhead = addr = parse_server(&share_name);
1434         if((addrhead == NULL) && (got_ip == 0)) {
1435                 fprintf(stderr, "No ip address specified and hostname not found\n");
1436                 rc = EX_USAGE;
1437                 goto mount_exit;
1438         }
1439         
1440         /* BB save off path and pop after mount returns? */
1441         resolved_path = (char *)malloc(PATH_MAX+1);
1442         if(resolved_path) {
1443                 /* Note that if we can not canonicalize the name, we get
1444                 another chance to see if it is valid when we chdir to it */
1445                 if (realpath(mountpoint, resolved_path)) {
1446                         mountpoint = resolved_path; 
1447                 }
1448         }
1449         if(got_user == 0) {
1450                 /* Note that the password will not be retrieved from the
1451                    USER env variable (ie user%password form) as there is
1452                    already a PASSWD environment varaible */
1453                 if (getenv("USER"))
1454                         user_name = strdup(getenv("USER"));
1455                 if (user_name == NULL)
1456                         user_name = getusername();
1457                 got_user = 1;
1458         }
1459        
1460         if(got_password == 0) {
1461                 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1462                                                            no good replacement yet. */
1463                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1464                 if (!tmp_pass || !mountpassword) {
1465                         fprintf(stderr, "Password not entered, exiting\n");
1466                         exit(EX_USAGE);
1467                 }
1468                 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1469                 got_password = 1;
1470         }
1471         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1472            remember to clear parms and overwrite password field before launching */
1473         if(orgoptions) {
1474                 optlen = strlen(orgoptions);
1475                 orgoptlen = optlen;
1476         } else
1477                 optlen = 0;
1478         if(share_name)
1479                 optlen += strlen(share_name) + 4;
1480         else {
1481                 fprintf(stderr, "No server share name specified\n");
1482                 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1483                 exit(EX_USAGE);
1484         }
1485         if(user_name)
1486                 optlen += strlen(user_name) + 6;
1487         optlen += MAX_ADDRESS_LEN + 4;
1488         if(mountpassword)
1489                 optlen += strlen(mountpassword) + 6;
1490 mount_retry:
1491         SAFE_FREE(options);
1492         options_size = optlen + 10 + DOMAIN_SIZE;
1493         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 */);
1494
1495         if(options == NULL) {
1496                 fprintf(stderr, "Could not allocate memory for mount options\n");
1497                 exit(EX_SYSERR);
1498         }
1499
1500         strlcpy(options, "unc=", options_size);
1501         strlcat(options,share_name,options_size);
1502         /* scan backwards and reverse direction of slash */
1503         temp = strrchr(options, '/');
1504         if(temp > options + 6)
1505                 *temp = '\\';
1506         if(user_name) {
1507                 /* check for syntax like user=domain\user */
1508                 if(got_domain == 0)
1509                         domain_name = check_for_domain(&user_name);
1510                 strlcat(options,",user=",options_size);
1511                 strlcat(options,user_name,options_size);
1512         }
1513         if(retry == 0) {
1514                 if(domain_name) {
1515                         /* extra length accounted for in option string above */
1516                         strlcat(options,",domain=",options_size);
1517                         strlcat(options,domain_name,options_size);
1518                 }
1519         }
1520
1521         strlcat(options,",ver=",options_size);
1522         strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1523
1524         if(orgoptions) {
1525                 strlcat(options,",",options_size);
1526                 strlcat(options,orgoptions,options_size);
1527         }
1528         if(prefixpath) {
1529                 strlcat(options,",prefixpath=",options_size);
1530                 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1531         }
1532
1533         /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1534         replace_char(dev_name, '\\', '/', strlen(share_name));
1535
1536         if (!got_ip && addr) {
1537                 strlcat(options, ",ip=", options_size);
1538                 current_len = strnlen(options, options_size);
1539                 optionstail = options + current_len;
1540                 switch (addr->ai_addr->sa_family) {
1541                 case AF_INET6:
1542                         addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1543                         ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1544                                            options_size - current_len);
1545                         break;
1546                 case AF_INET:
1547                         addr4 = (struct sockaddr_in *) addr->ai_addr;
1548                         ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1549                                            options_size - current_len);
1550                         break;
1551                 default:
1552                         ipaddr = NULL;
1553                 }
1554
1555                 /* if the address looks bogus, try the next one */
1556                 if (!ipaddr) {
1557                         addr = addr->ai_next;
1558                         if (addr)
1559                                 goto mount_retry;
1560                         rc = EX_SYSERR;
1561                         goto mount_exit;
1562                 }
1563         }
1564
1565         if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1566                 strlcat(options, "%", options_size);
1567                 current_len = strnlen(options, options_size);
1568                 optionstail = options + current_len;
1569                 snprintf(optionstail, options_size - current_len, "%u",
1570                          addr6->sin6_scope_id);
1571         }
1572
1573         if(verboseflag)
1574                 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1575
1576         if (mountpassword) {
1577                 /*
1578                  * Commas have to be doubled, or else they will
1579                  * look like the parameter separator
1580                  */
1581                 if(retry == 0)
1582                         check_for_comma(&mountpassword);
1583                 strlcat(options,",pass=",options_size);
1584                 strlcat(options,mountpassword,options_size);
1585                 if (verboseflag)
1586                         fprintf(stderr, ",pass=********");
1587         }
1588
1589         if (verboseflag)
1590                 fprintf(stderr, "\n");
1591
1592         if (!fakemnt && mount(dev_name, mountpoint, cifs_fstype, flags, options)) {
1593                 switch (errno) {
1594                 case ECONNREFUSED:
1595                 case EHOSTUNREACH:
1596                         if (addr) {
1597                                 addr = addr->ai_next;
1598                                 if (addr)
1599                                         goto mount_retry;
1600                         }
1601                         break;
1602                 case ENODEV:
1603                         fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1604                         break;
1605                 case ENXIO:
1606                         if(retry == 0) {
1607                                 retry = 1;
1608                                 if (uppercase_string(dev_name) &&
1609                                     uppercase_string(share_name) &&
1610                                     uppercase_string(prefixpath)) {
1611                                         fprintf(stderr, "retrying with upper case share name\n");
1612                                         goto mount_retry;
1613                                 }
1614                         }
1615                 }
1616                 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1617                 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1618                        "mount.cifs)\n");
1619                 rc = EX_FAIL;
1620                 goto mount_exit;
1621         }
1622
1623         if (nomtab)
1624                 goto mount_exit;
1625         atexit(unlock_mtab);
1626         rc = lock_mtab();
1627         if (rc) {
1628                 fprintf(stderr, "cannot lock mtab");
1629                 goto mount_exit;
1630         }
1631         pmntfile = setmntent(MOUNTED, "a+");
1632         if (!pmntfile) {
1633                 fprintf(stderr, "could not update mount table\n");
1634                 unlock_mtab();
1635                 rc = EX_FILEIO;
1636                 goto mount_exit;
1637         }
1638         mountent.mnt_fsname = dev_name;
1639         mountent.mnt_dir = mountpoint;
1640         mountent.mnt_type = (char *)(void *)cifs_fstype;
1641         mountent.mnt_opts = (char *)malloc(220);
1642         if(mountent.mnt_opts) {
1643                 char * mount_user = getusername();
1644                 memset(mountent.mnt_opts,0,200);
1645                 if(flags & MS_RDONLY)
1646                         strlcat(mountent.mnt_opts,"ro",220);
1647                 else
1648                         strlcat(mountent.mnt_opts,"rw",220);
1649                 if(flags & MS_MANDLOCK)
1650                         strlcat(mountent.mnt_opts,",mand",220);
1651                 if(flags & MS_NOEXEC)
1652                         strlcat(mountent.mnt_opts,",noexec",220);
1653                 if(flags & MS_NOSUID)
1654                         strlcat(mountent.mnt_opts,",nosuid",220);
1655                 if(flags & MS_NODEV)
1656                         strlcat(mountent.mnt_opts,",nodev",220);
1657                 if(flags & MS_SYNCHRONOUS)
1658                         strlcat(mountent.mnt_opts,",sync",220);
1659                 if(mount_user) {
1660                         if(getuid() != 0) {
1661                                 strlcat(mountent.mnt_opts,
1662                                         ",user=", 220);
1663                                 strlcat(mountent.mnt_opts,
1664                                         mount_user, 220);
1665                         }
1666                 }
1667         }
1668         mountent.mnt_freq = 0;
1669         mountent.mnt_passno = 0;
1670         rc = addmntent(pmntfile,&mountent);
1671         endmntent(pmntfile);
1672         unlock_mtab();
1673         SAFE_FREE(mountent.mnt_opts);
1674         if (rc)
1675                 rc = EX_FILEIO;
1676 mount_exit:
1677         if(mountpassword) {
1678                 int len = strlen(mountpassword);
1679                 memset(mountpassword,0,len);
1680                 SAFE_FREE(mountpassword);
1681         }
1682
1683         if (addrhead)
1684                 freeaddrinfo(addrhead);
1685         SAFE_FREE(options);
1686         SAFE_FREE(orgoptions);
1687         SAFE_FREE(resolved_path);
1688         SAFE_FREE(share_name);
1689         exit(rc);
1690 }