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