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