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