We don't need to call endpwent if we never call getpwent.
[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         char user[32];
359         char group[32];
360
361         if (!optionsp || !*optionsp)
362                 return 1;
363         data = *optionsp;
364
365         if(verboseflag)
366                 printf("parsing options: %s\n", data);
367
368         /* BB fixme check for separator override BB */
369
370         if (getuid()) {
371                 got_uid = 1;
372                 snprintf(user,sizeof(user),"%u",getuid());
373                 got_gid = 1;
374                 snprintf(group,sizeof(group),"%u",getgid());
375         }
376
377 /* while ((data = strsep(&options, ",")) != NULL) { */
378         while(data != NULL) {
379                 /*  check if ends with trailing comma */
380                 if(*data == 0)
381                         break;
382
383                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
384                 /* data  = next keyword */
385                 /* value = next value ie stuff after equal sign */
386
387                 next_keyword = strchr(data,','); /* BB handle sep= */
388         
389                 /* temporarily null terminate end of keyword=value pair */
390                 if(next_keyword)
391                         *next_keyword++ = 0;
392
393                 /* temporarily null terminate keyword to make keyword and value distinct */
394                 if ((value = strchr(data, '=')) != NULL) {
395                         *value = '\0';
396                         value++;
397                 }
398
399                 if (strncmp(data, "users",5) == 0) {
400                         if(!value || !*value) {
401                                 goto nocopy;
402                         }
403                 } else if (strncmp(data, "user_xattr",10) == 0) {
404                    /* do nothing - need to skip so not parsed as user name */
405                 } else if (strncmp(data, "user", 4) == 0) {
406
407                         if (!value || !*value) {
408                                 if(data[4] == '\0') {
409                                         if(verboseflag)
410                                                 printf("\nskipping empty user mount parameter\n");
411                                         /* remove the parm since it would otherwise be confusing
412                                         to the kernel code which would think it was a real username */
413                                         goto nocopy;
414                                 } else {
415                                         printf("username specified with no parameter\n");
416                                         return 1;       /* needs_arg; */
417                                 }
418                         } else {
419                                 if (strnlen(value, 260) < 260) {
420                                         got_user=1;
421                                         percent_char = strchr(value,'%');
422                                         if(percent_char) {
423                                                 *percent_char = ',';
424                                                 if(mountpassword == NULL)
425                                                         mountpassword = (char *)calloc(65,1);
426                                                 if(mountpassword) {
427                                                         if(got_password)
428                                                                 printf("\nmount.cifs warning - password specified twice\n");
429                                                         got_password = 1;
430                                                         percent_char++;
431                                                         strncpy(mountpassword, percent_char,64);
432                                                 /*  remove password from username */
433                                                         while(*percent_char != 0) {
434                                                                 *percent_char = ',';
435                                                                 percent_char++;
436                                                         }
437                                                 }
438                                         }
439                                         /* this is only case in which the user
440                                         name buf is not malloc - so we have to
441                                         check for domain name embedded within
442                                         the user name here since the later
443                                         call to check_for_domain will not be
444                                         invoked */
445                                         domain_name = check_for_domain(&value);
446                                 } else {
447                                         printf("username too long\n");
448                                         return 1;
449                                 }
450                         }
451                 } else if (strncmp(data, "pass", 4) == 0) {
452                         if (!value || !*value) {
453                                 if(got_password) {
454                                         printf("\npassword specified twice, ignoring second\n");
455                                 } else
456                                         got_password = 1;
457                         } else if (strnlen(value, 17) < 17) {
458                                 if(got_password)
459                                         printf("\nmount.cifs warning - password specified twice\n");
460                                 got_password = 1;
461                         } else {
462                                 printf("password too long\n");
463                                 return 1;
464                         }
465                 } else if (strncmp(data, "sec", 3) == 0) {
466                         if (value) {
467                                 if (!strcmp(value, "none"))
468                                         got_password = 1;
469                         }
470                 } else if (strncmp(data, "ip", 2) == 0) {
471                         if (!value || !*value) {
472                                 printf("target ip address argument missing");
473                         } else if (strnlen(value, 35) < 35) {
474                                 if(verboseflag)
475                                         printf("ip address %s override specified\n",value);
476                                 got_ip = 1;
477                         } else {
478                                 printf("ip address too long\n");
479                                 return 1;
480                         }
481                 } else if ((strncmp(data, "unc", 3) == 0)
482                    || (strncmp(data, "target", 6) == 0)
483                    || (strncmp(data, "path", 4) == 0)) {
484                         if (!value || !*value) {
485                                 printf("invalid path to network resource\n");
486                                 return 1;  /* needs_arg; */
487                         } else if(strnlen(value,5) < 5) {
488                                 printf("UNC name too short");
489                         }
490
491                         if (strnlen(value, 300) < 300) {
492                                 got_unc = 1;
493                                 if (strncmp(value, "//", 2) == 0) {
494                                         if(got_unc)
495                                                 printf("unc name specified twice, ignoring second\n");
496                                         else
497                                                 got_unc = 1;
498                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
499                                         printf("UNC Path does not begin with // or \\\\ \n");
500                                         return 1;
501                                 } else {
502                                         if(got_unc)
503                                                 printf("unc name specified twice, ignoring second\n");
504                                         else
505                                                 got_unc = 1;
506                                 }
507                         } else {
508                                 printf("CIFS: UNC name too long\n");
509                                 return 1;
510                         }
511                 } else if ((strncmp(data, "domain", 3) == 0)
512                            || (strncmp(data, "workgroup", 5) == 0)) {
513                         if (!value || !*value) {
514                                 printf("CIFS: invalid domain name\n");
515                                 return 1;       /* needs_arg; */
516                         }
517                         if (strnlen(value, 65) < 65) {
518                                 got_domain = 1;
519                         } else {
520                                 printf("domain name too long\n");
521                                 return 1;
522                         }
523                 } else if (strncmp(data, "cred", 4) == 0) {
524                         if (value && *value) {
525                                 rc = open_cred_file(value);
526                                 if(rc) {
527                                         printf("error %d opening credential file %s\n",rc, value);
528                                         return 1;
529                                 }
530                         } else {
531                                 printf("invalid credential file name specified\n");
532                                 return 1;
533                         }
534                 } else if (strncmp(data, "uid", 3) == 0) {
535                         if (value && *value) {
536                                 got_uid = 1;
537                                 if (!isdigit(*value)) {
538                                         struct passwd *pw;
539
540                                         if (!(pw = getpwnam(value))) {
541                                                 printf("bad user name \"%s\"\n", value);
542                                                 exit(1);
543                                         }
544                                         snprintf(user, sizeof(user), "%u", pw->pw_uid);
545                                 } else {
546                                         strlcpy(user,value,sizeof(user));
547                                 }
548                         }
549                         goto nocopy;
550                 } else if (strncmp(data, "gid", 3) == 0) {
551                         if (value && *value) {
552                                 got_gid = 1;
553                                 if (!isdigit(*value)) {
554                                         struct group *gr;
555
556                                         if (!(gr = getgrnam(value))) {
557                                                 printf("bad group name \"%s\"\n", value);
558                                                 exit(1);
559                                         }
560                                         snprintf(group, sizeof(group), "%u", gr->gr_gid);
561                                 } else {
562                                         strlcpy(group,value,sizeof(group));
563                                 }
564                         }
565                         goto nocopy;
566        /* fmask and dmask synonyms for people used to smbfs syntax */
567                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
568                         if (!value || !*value) {
569                                 printf ("Option '%s' requires a numerical argument\n", data);
570                                 return 1;
571                         }
572
573                         if (value[0] != '0') {
574                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
575                         }
576
577                         if (strcmp (data, "fmask") == 0) {
578                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
579                                 data = "file_mode"; /* BB fix this */
580                         }
581                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
582                         if (!value || !*value) {
583                                 printf ("Option '%s' requires a numerical argument\n", data);
584                                 return 1;
585                         }
586
587                         if (value[0] != '0') {
588                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
589                         }
590
591                         if (strcmp (data, "dmask") == 0) {
592                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
593                                 data = "dir_mode";
594                         }
595                         /* the following eight mount options should be
596                         stripped out from what is passed into the kernel
597                         since these eight options are best passed as the
598                         mount flags rather than redundantly to the kernel 
599                         and could generate spurious warnings depending on the
600                         level of the corresponding cifs vfs kernel code */
601                 } else if (strncmp(data, "nosuid", 6) == 0) {
602                         *filesys_flags |= MS_NOSUID;
603                 } else if (strncmp(data, "suid", 4) == 0) {
604                         *filesys_flags &= ~MS_NOSUID;
605                 } else if (strncmp(data, "nodev", 5) == 0) {
606                         *filesys_flags |= MS_NODEV;
607                 } else if ((strncmp(data, "nobrl", 5) == 0) || 
608                            (strncmp(data, "nolock", 6) == 0)) {
609                         *filesys_flags &= ~MS_MANDLOCK;
610                 } else if (strncmp(data, "dev", 3) == 0) {
611                         *filesys_flags &= ~MS_NODEV;
612                 } else if (strncmp(data, "noexec", 6) == 0) {
613                         *filesys_flags |= MS_NOEXEC;
614                 } else if (strncmp(data, "exec", 4) == 0) {
615                         *filesys_flags &= ~MS_NOEXEC;
616                 } else if (strncmp(data, "guest", 5) == 0) {
617                         got_password=1;
618                 } else if (strncmp(data, "ro", 2) == 0) {
619                         *filesys_flags |= MS_RDONLY;
620                 } else if (strncmp(data, "rw", 2) == 0) {
621                         *filesys_flags &= ~MS_RDONLY;
622                 } else if (strncmp(data, "remount", 7) == 0) {
623                         *filesys_flags |= MS_REMOUNT;
624                 } /* else if (strnicmp(data, "port", 4) == 0) {
625                         if (value && *value) {
626                                 vol->port =
627                                         simple_strtoul(value, &value, 0);
628                         }
629                 } else if (strnicmp(data, "rsize", 5) == 0) {
630                         if (value && *value) {
631                                 vol->rsize =
632                                         simple_strtoul(value, &value, 0);
633                         }
634                 } else if (strnicmp(data, "wsize", 5) == 0) {
635                         if (value && *value) {
636                                 vol->wsize =
637                                         simple_strtoul(value, &value, 0);
638                         }
639                 } else if (strnicmp(data, "version", 3) == 0) {
640                 } else {
641                         printf("CIFS: Unknown mount option %s\n",data);
642                 } */ /* nothing to do on those four mount options above.
643                         Just pass to kernel and ignore them here */
644
645                 /* Copy (possibly modified) option to out */
646                 word_len = strlen(data);
647                 if (value)
648                         word_len += 1 + strlen(value);
649
650                 out = (char *)realloc(out, out_len + word_len + 2);
651                 if (out == NULL) {
652                         perror("malloc");
653                         exit(1);
654                 }
655
656                 if (out_len) {
657                         strlcat(out, ",", out_len + word_len + 2);
658                         out_len++;
659                 }
660
661                 if (value)
662                         snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
663                 else
664                         snprintf(out + out_len, word_len + 1, "%s", data);
665                 out_len = strlen(out);
666
667 nocopy:
668                 data = next_keyword;
669         }
670
671         /* special-case the uid and gid */
672         if (got_uid) {
673                 word_len = strlen(user);
674
675                 out = (char *)realloc(out, out_len + word_len + 6);
676                 if (out == NULL) {
677                         perror("malloc");
678                         exit(1);
679                 }
680
681                 if (out_len) {
682                         strlcat(out, ",", out_len + word_len + 6);
683                         out_len++;
684                 }
685                 snprintf(out + out_len, word_len + 5, "uid=%s", user);
686                 out_len = strlen(out);
687         }
688         if (got_gid) {
689                 word_len = strlen(group);
690
691                 out = (char *)realloc(out, out_len + 1 + word_len + 6);
692                 if (out == NULL) {
693                 perror("malloc");
694                         exit(1);
695                 }
696
697                 if (out_len) {
698                         strlcat(out, ",", out_len + word_len + 6);
699                         out_len++;
700                 }
701                 snprintf(out + out_len, word_len + 5, "gid=%s", group);
702                 out_len = strlen(out);
703         }
704
705         free(*optionsp);
706         *optionsp = out;
707         return 0;
708 }
709
710 /* replace all (one or more) commas with double commas */
711 static void check_for_comma(char ** ppasswrd)
712 {
713         char *new_pass_buf;
714         char *pass;
715         int i,j;
716         int number_of_commas = 0;
717         int len;
718
719         if(ppasswrd == NULL)
720                 return;
721         else 
722                 (pass = *ppasswrd);
723
724         len = strlen(pass);
725
726         for(i=0;i<len;i++)  {
727                 if(pass[i] == ',')
728                         number_of_commas++;
729         }
730
731         if(number_of_commas == 0)
732                 return;
733         if(number_of_commas > 64) {
734                 /* would otherwise overflow the mount options buffer */
735                 printf("\nInvalid password. Password contains too many commas.\n");
736                 return;
737         }
738
739         new_pass_buf = (char *)malloc(len+number_of_commas+1);
740         if(new_pass_buf == NULL)
741                 return;
742
743         for(i=0,j=0;i<len;i++,j++) {
744                 new_pass_buf[j] = pass[i];
745                 if(pass[i] == ',') {
746                         j++;
747                         new_pass_buf[j] = pass[i];
748                 }
749         }
750         new_pass_buf[len+number_of_commas] = 0;
751
752         free(*ppasswrd);
753         *ppasswrd = new_pass_buf;
754         
755         return;
756 }
757
758 /* Usernames can not have backslash in them and we use
759    [BB check if usernames can have forward slash in them BB] 
760    backslash as domain\user separator character
761 */
762 static char * check_for_domain(char **ppuser)
763 {
764         char * original_string;
765         char * usernm;
766         char * domainnm;
767         int    original_len;
768         int    len;
769         int    i;
770
771         if(ppuser == NULL)
772                 return NULL;
773
774         original_string = *ppuser;
775
776         if (original_string == NULL)
777                 return NULL;
778         
779         original_len = strlen(original_string);
780
781         usernm = strchr(*ppuser,'/');
782         if (usernm == NULL) {
783                 usernm = strchr(*ppuser,'\\');
784                 if (usernm == NULL)
785                         return NULL;
786         }
787
788         if(got_domain) {
789                 printf("Domain name specified twice. Username probably malformed\n");
790                 return NULL;
791         }
792
793         usernm[0] = 0;
794         domainnm = *ppuser;
795         if (domainnm[0] != 0) {
796                 got_domain = 1;
797         } else {
798                 printf("null domain\n");
799         }
800         len = strlen(domainnm);
801         /* reset domainm to new buffer, and copy
802         domain name into it */
803         domainnm = (char *)malloc(len+1);
804         if(domainnm == NULL)
805                 return NULL;
806
807         strlcpy(domainnm,*ppuser,len+1);
808
809 /*      move_string(*ppuser, usernm+1) */
810         len = strlen(usernm+1);
811
812         if(len >= original_len) {
813                 /* should not happen */
814                 return domainnm;
815         }
816
817         for(i=0;i<original_len;i++) {
818                 if(i<len)
819                         original_string[i] = usernm[i+1];
820                 else /* stuff with commas to remove last parm */
821                         original_string[i] = ',';
822         }
823
824         /* BB add check for more than one slash? 
825           strchr(*ppuser,'/');
826           strchr(*ppuser,'\\') 
827         */
828         
829         return domainnm;
830 }
831
832 /* Note that caller frees the returned buffer if necessary */
833 static char * parse_server(char ** punc_name)
834 {
835         char * unc_name = *punc_name;
836         int length = strnlen(unc_name,1024);
837         char * share;
838         char * ipaddress_string = NULL;
839         struct hostent * host_entry = NULL;
840         struct in_addr server_ipaddr;
841
842         if(length > 1023) {
843                 printf("mount error: UNC name too long");
844                 return NULL;
845         }
846         if (strncasecmp("cifs://",unc_name,7) == 0)
847                 return parse_cifs_url(unc_name+7);
848         if (strncasecmp("smb://",unc_name,6) == 0) {
849                 return parse_cifs_url(unc_name+6);
850         }
851
852         if(length < 3) {
853                 /* BB add code to find DFS root here */
854                 printf("\nMounting the DFS root for domain not implemented yet\n");
855                 return NULL;
856         } else {
857                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
858                         /* check for nfs syntax ie server:share */
859                         share = strchr(unc_name,':');
860                         if(share) {
861                                 free_share_name = 1;
862                                 *punc_name = (char *)malloc(length+3);
863                                 if(*punc_name == NULL) {
864                                         /* put the original string back  if 
865                                            no memory left */
866                                         *punc_name = unc_name;
867                                         return NULL;
868                                 }
869                                         
870                                 *share = '/';
871                                 strncpy((*punc_name)+2,unc_name,length);
872                                 unc_name = *punc_name;
873                                 unc_name[length+2] = 0;
874                                 goto continue_unc_parsing;
875                         } else {
876                                 printf("mount error: improperly formatted UNC name.");
877                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
878                                 return NULL;
879                         }
880                 } else {
881 continue_unc_parsing:
882                         unc_name[0] = '/';
883                         unc_name[1] = '/';
884                         unc_name += 2;
885                         if ((share = strchr(unc_name, '/')) || 
886                                 (share = strchr(unc_name,'\\'))) {
887                                 *share = 0;  /* temporarily terminate the string */
888                                 share += 1;
889                                 if(got_ip == 0) {
890                                         host_entry = gethostbyname(unc_name);
891                                 }
892                                 *(share - 1) = '/'; /* put the slash back */
893                                 if ((prefixpath = strchr(share, '/'))) {
894                                         *prefixpath = 0;  /* permanently terminate the string */
895                                         if (!strlen(++prefixpath))
896                                                 prefixpath = NULL; /* this needs to be done explicitly */
897                                 }
898                                 if(got_ip) {
899                                         if(verboseflag)
900                                                 printf("ip address specified explicitly\n");
901                                         return NULL;
902                                 }
903                                 if(host_entry == NULL) {
904                                         printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
905                                         return NULL;
906                                 } else {
907                                         /* BB should we pass an alternate version of the share name as Unicode */
908                                         /* BB what about ipv6? BB */
909                                         /* BB add retries with alternate servers in list */
910
911                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
912
913                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
914                                         if(ipaddress_string == NULL) {
915                                                 printf("mount error: could not get valid ip address for target server\n");
916                                                 return NULL;
917                                         }
918                                         return ipaddress_string; 
919                                 }
920                         } else {
921                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
922                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
923                                 return NULL;
924                         }
925                 }
926         }
927 }
928
929 static struct option longopts[] = {
930         { "all", 0, NULL, 'a' },
931         { "help",0, NULL, 'h' },
932         { "move",0, NULL, 'm' },
933         { "bind",0, NULL, 'b' },
934         { "read-only", 0, NULL, 'r' },
935         { "ro", 0, NULL, 'r' },
936         { "verbose", 0, NULL, 'v' },
937         { "version", 0, NULL, 'V' },
938         { "read-write", 0, NULL, 'w' },
939         { "rw", 0, NULL, 'w' },
940         { "options", 1, NULL, 'o' },
941         { "type", 1, NULL, 't' },
942         { "rsize",1, NULL, 'R' },
943         { "wsize",1, NULL, 'W' },
944         { "uid", 1, NULL, '1'},
945         { "gid", 1, NULL, '2'},
946         { "user",1,NULL,'u'},
947         { "username",1,NULL,'u'},
948         { "dom",1,NULL,'d'},
949         { "domain",1,NULL,'d'},
950         { "password",1,NULL,'p'},
951         { "pass",1,NULL,'p'},
952         { "credentials",1,NULL,'c'},
953         { "port",1,NULL,'P'},
954         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
955         { NULL, 0, NULL, 0 }
956 };
957
958 int main(int argc, char ** argv)
959 {
960         int c;
961         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
962         char * orgoptions = NULL;
963         char * share_name = NULL;
964         char * ipaddr = NULL;
965         char * uuid = NULL;
966         char * mountpoint = NULL;
967         char * options = NULL;
968         char * resolved_path = NULL;
969         char * temp;
970         int rc;
971         int rsize = 0;
972         int wsize = 0;
973         int nomtab = 0;
974         int uid = 0;
975         int gid = 0;
976         int optlen = 0;
977         int orgoptlen = 0;
978         size_t options_size = 0;
979         int retry = 0; /* set when we have to retry mount with uppercase */
980         struct stat statbuf;
981         struct utsname sysinfo;
982         struct mntent mountent;
983         FILE * pmntfile;
984
985         /* setlocale(LC_ALL, "");
986         bindtextdomain(PACKAGE, LOCALEDIR);
987         textdomain(PACKAGE); */
988
989         if(argc && argv) {
990                 thisprogram = argv[0];
991         } else {
992                 mount_cifs_usage();
993                 exit(1);
994         }
995
996         if(thisprogram == NULL)
997                 thisprogram = "mount.cifs";
998
999         uname(&sysinfo);
1000         /* BB add workstation name and domain and pass down */
1001
1002 /* #ifdef _GNU_SOURCE
1003         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1004 #endif */
1005         if(argc > 2) {
1006                 share_name = argv[1];
1007                 mountpoint = argv[2];
1008         }
1009
1010         /* add sharename in opts string as unc= parm */
1011
1012         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1013                          longopts, NULL)) != -1) {
1014                 switch (c) {
1015 /* No code to do the following  options yet */
1016 /*      case 'l':
1017                 list_with_volumelabel = 1;
1018                 break;
1019         case 'L':
1020                 volumelabel = optarg;
1021                 break; */
1022 /*      case 'a':              
1023                 ++mount_all;
1024                 break; */
1025
1026                 case '?':
1027                 case 'h':        /* help */
1028                         mount_cifs_usage ();
1029                         exit(1);
1030                 case 'n':
1031                     ++nomtab;
1032                     break;
1033                 case 'b':
1034 #ifdef MS_BIND
1035                         flags |= MS_BIND;
1036 #else
1037                         fprintf(stderr,
1038                                 "option 'b' (MS_BIND) not supported\n");
1039 #endif
1040                         break;
1041                 case 'm':
1042 #ifdef MS_MOVE                
1043                         flags |= MS_MOVE;
1044 #else
1045                         fprintf(stderr,
1046                                 "option 'm' (MS_MOVE) not supported\n");
1047 #endif
1048                         break;
1049                 case 'o':
1050                         orgoptions = strdup(optarg);
1051                     break;
1052                 case 'r':  /* mount readonly */
1053                         flags |= MS_RDONLY;
1054                         break;
1055                 case 'U':
1056                         uuid = optarg;
1057                         break;
1058                 case 'v':
1059                         ++verboseflag;
1060                         break;
1061                 case 'V':          
1062                         printf ("mount.cifs version: %s.%s%s\n",
1063                         MOUNT_CIFS_VERSION_MAJOR,
1064                         MOUNT_CIFS_VERSION_MINOR,
1065                         MOUNT_CIFS_VENDOR_SUFFIX);
1066                         if(mountpassword) {
1067                                 memset(mountpassword,0,64);
1068                         }
1069                         exit (0);
1070                 case 'w':
1071                         flags &= ~MS_RDONLY;
1072                         break;
1073                 case 'R':
1074                         rsize = atoi(optarg) ;
1075                         break;
1076                 case 'W':
1077                         wsize = atoi(optarg);
1078                         break;
1079                 case '1':
1080                         if (isdigit(*optarg)) {
1081                                 char *ep;
1082
1083                                 uid = strtoul(optarg, &ep, 10);
1084                                 if (*ep) {
1085                                         printf("bad uid value \"%s\"\n", optarg);
1086                                         exit(1);
1087                                 }
1088                         } else {
1089                                 struct passwd *pw;
1090
1091                                 if (!(pw = getpwnam(optarg))) {
1092                                         printf("bad user name \"%s\"\n", optarg);
1093                                         exit(1);
1094                                 }
1095                                 uid = pw->pw_uid;
1096                                 endpwent();
1097                         }
1098                         break;
1099                 case '2':
1100                         if (isdigit(*optarg)) {
1101                                 char *ep;
1102
1103                                 gid = strtoul(optarg, &ep, 10);
1104                                 if (*ep) {
1105                                         printf("bad gid value \"%s\"\n", optarg);
1106                                         exit(1);
1107                                 }
1108                         } else {
1109                                 struct group *gr;
1110
1111                                 if (!(gr = getgrnam(optarg))) {
1112                                         printf("bad user name \"%s\"\n", optarg);
1113                                         exit(1);
1114                                 }
1115                                 gid = gr->gr_gid;
1116                                 endpwent();
1117                         }
1118                         break;
1119                 case 'u':
1120                         got_user = 1;
1121                         user_name = optarg;
1122                         break;
1123                 case 'd':
1124                         domain_name = optarg; /* BB fix this - currently ignored */
1125                         got_domain = 1;
1126                         break;
1127                 case 'p':
1128                         if(mountpassword == NULL)
1129                                 mountpassword = (char *)calloc(65,1);
1130                         if(mountpassword) {
1131                                 got_password = 1;
1132                                 strncpy(mountpassword,optarg,64);
1133                         }
1134                         break;
1135                 case 'S':
1136                         get_password_from_file(0 /* stdin */,NULL);
1137                         break;
1138                 case 't':
1139                         break;
1140                 default:
1141                         printf("unknown mount option %c\n",c);
1142                         mount_cifs_usage();
1143                         exit(1);
1144                 }
1145         }
1146
1147         if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1148                 mount_cifs_usage();
1149                 exit(1);
1150         }
1151
1152         if (getenv("PASSWD")) {
1153                 if(mountpassword == NULL)
1154                         mountpassword = (char *)calloc(65,1);
1155                 if(mountpassword) {
1156                         strncpy(mountpassword,getenv("PASSWD"),64);
1157                         got_password = 1;
1158                 }
1159         } else if (getenv("PASSWD_FD")) {
1160                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1161         } else if (getenv("PASSWD_FILE")) {
1162                 get_password_from_file(0, getenv("PASSWD_FILE"));
1163         }
1164
1165         if (orgoptions && parse_options(&orgoptions, &flags)) {
1166                 rc = -1;
1167                 goto mount_exit;
1168         }
1169         ipaddr = parse_server(&share_name);
1170         if((ipaddr == NULL) && (got_ip == 0)) {
1171                 printf("No ip address specified and hostname not found\n");
1172                 rc = -1;
1173                 goto mount_exit;
1174         }
1175         
1176         /* BB save off path and pop after mount returns? */
1177         resolved_path = (char *)malloc(PATH_MAX+1);
1178         if(resolved_path) {
1179                 /* Note that if we can not canonicalize the name, we get
1180                 another chance to see if it is valid when we chdir to it */
1181                 if (realpath(mountpoint, resolved_path)) {
1182                         mountpoint = resolved_path; 
1183                 }
1184         }
1185         if(chdir(mountpoint)) {
1186                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1187                 rc = -1;
1188                 goto mount_exit;
1189         }
1190
1191         if(stat (".", &statbuf)) {
1192                 printf("mount error: mount point %s does not exist\n",mountpoint);
1193                 rc = -1;
1194                 goto mount_exit;
1195         }
1196
1197         if (S_ISDIR(statbuf.st_mode) == 0) {
1198                 printf("mount error: mount point %s is not a directory\n",mountpoint);
1199                 rc = -1;
1200                 goto mount_exit;
1201         }
1202
1203         if((getuid() != 0) && (geteuid() == 0)) {
1204                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1205 #ifndef CIFS_ALLOW_USR_SUID
1206                         /* Do not allow user mounts to control suid flag
1207                         for mount unless explicitly built that way */
1208                         flags |= MS_NOSUID | MS_NODEV;
1209 #endif                                          
1210                 } else {
1211                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
1212                         return -1;
1213                 }
1214         }
1215
1216         if(got_user == 0) {
1217                 user_name = getusername();
1218                 got_user = 1;
1219         }
1220        
1221         if(got_password == 0) {
1222                 mountpassword = getpass("Password: "); /* BB obsolete */
1223                 got_password = 1;
1224         }
1225         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1226            remember to clear parms and overwrite password field before launching */
1227 mount_retry:
1228         if(orgoptions) {
1229                 optlen = strlen(orgoptions);
1230                 orgoptlen = optlen;
1231         } else
1232                 optlen = 0;
1233         if(share_name)
1234                 optlen += strlen(share_name) + 4;
1235         else {
1236                 printf("No server share name specified\n");
1237                 printf("\nMounting the DFS root for server not implemented yet\n");
1238                 exit(1);
1239         }
1240         if(user_name)
1241                 optlen += strlen(user_name) + 6;
1242         if(ipaddr)
1243                 optlen += strlen(ipaddr) + 4;
1244         if(mountpassword)
1245                 optlen += strlen(mountpassword) + 6;
1246         if(options)
1247                 free(options);
1248         options_size = optlen + 10 + 64;
1249         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 */);
1250
1251         if(options == NULL) {
1252                 printf("Could not allocate memory for mount options\n");
1253                 return -1;
1254         }
1255
1256         options[0] = 0;
1257         strlcpy(options,"unc=",options_size);
1258         strlcat(options,share_name,options_size);
1259         /* scan backwards and reverse direction of slash */
1260         temp = strrchr(options, '/');
1261         if(temp > options + 6)
1262                 *temp = '\\';
1263         if(ipaddr) {
1264                 strlcat(options,",ip=",options_size);
1265                 strlcat(options,ipaddr,options_size);
1266         }
1267
1268         if(user_name) {
1269                 /* check for syntax like user=domain\user */
1270                 if(got_domain == 0)
1271                         domain_name = check_for_domain(&user_name);
1272                 strlcat(options,",user=",options_size);
1273                 strlcat(options,user_name,options_size);
1274         }
1275         if(retry == 0) {
1276                 if(domain_name) {
1277                         /* extra length accounted for in option string above */
1278                         strlcat(options,",domain=",options_size);
1279                         strlcat(options,domain_name,options_size);
1280                 }
1281         }
1282         if(mountpassword) {
1283                 /* Commas have to be doubled, or else they will
1284                 look like the parameter separator */
1285 /*              if(sep is not set)*/
1286                 if(retry == 0)
1287                         check_for_comma(&mountpassword);
1288                 strlcat(options,",pass=",options_size);
1289                 strlcat(options,mountpassword,options_size);
1290         }
1291
1292         strlcat(options,",ver=",options_size);
1293         strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1294
1295         if(orgoptions) {
1296                 strlcat(options,",",options_size);
1297                 strlcat(options,orgoptions,options_size);
1298         }
1299         if(prefixpath) {
1300                 strlcat(options,",prefixpath=",options_size);
1301                 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1302         }
1303         if(verboseflag)
1304                 printf("\nmount.cifs kernel mount options %s \n",options);
1305         if(mount(share_name, mountpoint, "cifs", flags, options)) {
1306         /* remember to kill daemon on error */
1307                 char * tmp;
1308
1309                 switch (errno) {
1310                 case 0:
1311                         printf("mount failed but no error number set\n");
1312                         break;
1313                 case ENODEV:
1314                         printf("mount error: cifs filesystem not supported by the system\n");
1315                         break;
1316                 case ENXIO:
1317                         if(retry == 0) {
1318                                 retry = 1;
1319                                 tmp = share_name;
1320                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1321                                         *tmp = toupper((unsigned char)*tmp);
1322                                         tmp++;
1323                                 }
1324                                 if(!*tmp) {
1325                                         printf("retrying with upper case share name\n");
1326                                         goto mount_retry;
1327                                 }
1328                         }
1329                 default:
1330                         printf("mount error %d = %s\n",errno,strerror(errno));
1331                 }
1332                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1333                 rc = -1;
1334                 goto mount_exit;
1335         } else {
1336                 pmntfile = setmntent(MOUNTED, "a+");
1337                 if(pmntfile) {
1338                         mountent.mnt_fsname = share_name;
1339                         mountent.mnt_dir = mountpoint;
1340                         mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1341                         mountent.mnt_opts = (char *)malloc(220);
1342                         if(mountent.mnt_opts) {
1343                                 char * mount_user = getusername();
1344                                 memset(mountent.mnt_opts,0,200);
1345                                 if(flags & MS_RDONLY)
1346                                         strlcat(mountent.mnt_opts,"ro",220);
1347                                 else
1348                                         strlcat(mountent.mnt_opts,"rw",220);
1349                                 if(flags & MS_MANDLOCK)
1350                                         strlcat(mountent.mnt_opts,",mand",220);
1351                                 if(flags & MS_NOEXEC)
1352                                         strlcat(mountent.mnt_opts,",noexec",220);
1353                                 if(flags & MS_NOSUID)
1354                                         strlcat(mountent.mnt_opts,",nosuid",220);
1355                                 if(flags & MS_NODEV)
1356                                         strlcat(mountent.mnt_opts,",nodev",220);
1357                                 if(flags & MS_SYNCHRONOUS)
1358                                         strlcat(mountent.mnt_opts,",synch",220);
1359                                 if(mount_user) {
1360                                         if(getuid() != 0) {
1361                                                 strlcat(mountent.mnt_opts,",user=",220);
1362                                                 strlcat(mountent.mnt_opts,mount_user,220);
1363                                         }
1364                                         /* free(mount_user); do not free static mem */
1365                                 }
1366                         }
1367                         mountent.mnt_freq = 0;
1368                         mountent.mnt_passno = 0;
1369                         rc = addmntent(pmntfile,&mountent);
1370                         endmntent(pmntfile);
1371                         if(mountent.mnt_opts)
1372                                 free(mountent.mnt_opts);
1373                 } else {
1374                     printf("could not update mount table\n");
1375                 }
1376         }
1377         rc = 0;
1378 mount_exit:
1379         if(mountpassword) {
1380                 int len = strlen(mountpassword);
1381                 memset(mountpassword,0,len);
1382                 free(mountpassword);
1383         }
1384
1385         if(options) {
1386                 memset(options,0,optlen);
1387                 free(options);
1388         }
1389
1390         if(orgoptions) {
1391                 memset(orgoptions,0,orgoptlen);
1392                 free(orgoptions);
1393         }
1394         if(resolved_path) {
1395                 free(resolved_path);
1396         }
1397
1398         if(free_share_name) {
1399                 free(share_name);
1400                 }
1401         return rc;
1402 }