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