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