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