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