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