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