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