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