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