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