root/trunk/freewrt/package/cifsmount/mount.cifs.c

Revision 1, 24.8 kB (checked in by wbx, 3 years ago)

add OpenWrt? trunk revision 3830.

Line 
1 /*
2    Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3    Copyright (C) 2003 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 <sys/types.h>
28 #include <sys/mount.h>
29 #include <sys/stat.h>
30 #include <sys/utsname.h>
31 #include <sys/socket.h>
32 #include <arpa/inet.h>
33 #include <getopt.h>
34 #include <errno.h>
35 #include <netdb.h>
36 #include <string.h>
37 #include <mntent.h>
38 #include <fcntl.h>
39
40 #define MOUNT_CIFS_VERSION_MAJOR "1"
41 #define MOUNT_CIFS_VERSION_MINOR "5"
42
43 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
44 #define MOUNT_CIFS_VENDOR_SUFFIX ""
45 #endif
46
47 #ifndef MS_MOVE
48 #define MS_MOVE 8192
49 #endif
50
51 char * thisprogram;
52 int verboseflag = 0;
53 static int got_password = 0;
54 static int got_user = 0;
55 static int got_domain = 0;
56 static int got_ip = 0;
57 static int got_unc = 0;
58 static int got_uid = 0;
59 static int got_gid = 0;
60 static int free_share_name = 0;
61 static char * user_name = NULL;
62 char * mountpassword = NULL;
63
64
65 /* BB finish BB
66
67         cifs_umount
68         open nofollow - avoid symlink exposure?
69         get owner of dir see if matches self or if root
70         call system(umount argv) etc.
71                 
72 BB end finish BB */
73
74 static void mount_cifs_usage(void)
75 {
76         printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
77         printf("\nMount the remote target, specified as a UNC name,");
78         printf(" to a local directory.\n\nOptions:\n");
79         printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
80         printf("\nLess commonly used options:");
81         printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,\n\trw,ro,sep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec");
82         printf("\n\nOptions not needed for servers supporting CIFS Unix extensions (e.g. most Samba versions):");
83         printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
84         printf("\n\nRarely used options:");
85         printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,dev,nodev");
86         printf("\n\nOptions are described in more detail in the manual page");
87         printf("\n\tman 8 mount.cifs\n");
88         printf("\nTo display the version number of the mount helper:");
89         printf("\n\t%s -V\n",thisprogram);
90
91         if(mountpassword) {
92                 memset(mountpassword,0,64);
93                 free(mountpassword);
94         }
95         exit(1);
96 }
97
98 /* caller frees username if necessary */
99 static char * getusername(void) {
100         char *username = NULL;
101         struct passwd *password = getpwuid(getuid());
102
103         if (password) {
104                 username = password->pw_name;
105         }
106         return username;
107 }
108
109 char * parse_cifs_url(char * unc_name)
110 {
111         printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
112         return NULL;
113 }
114
115 static int open_cred_file(char * file_name)
116 {
117         char * line_buf;
118         char * temp_val;
119         FILE * fs;
120         int i, length;
121         fs = fopen(file_name,"r");
122         if(fs == NULL)
123                 return errno;
124         line_buf = malloc(4096);
125         if(line_buf == NULL)
126                 return -ENOMEM;
127
128         while(fgets(line_buf,4096,fs)) {
129                 /* parse line from credential file */
130
131                 /* eat leading white space */
132                 for(i=0;i<4086;i++) {
133                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
134                                 break;
135                         /* if whitespace - skip past it */
136                 }
137                 if (strncasecmp("username",line_buf+i,8) == 0) {
138                         temp_val = strchr(line_buf + i,'=');
139                         if(temp_val) {
140                                 /* go past equals sign */
141                                 temp_val++;
142                                 for(length = 0;length<4087;length++) {
143                                         if(temp_val[length] == '\n')
144                                                 break;
145                                 }
146                                 if(length > 4086) {
147                                         printf("mount.cifs failed due to malformed username in credentials file");
148                                         memset(line_buf,0,4096);
149                                         if(mountpassword) {
150                                                 memset(mountpassword,0,64);
151                                         }
152                                         exit(1);
153                                 } else {
154                                         got_user = 1;
155                                         user_name = calloc(1 + length,1);
156                                         /* BB adding free of user_name string before exit,
157                                                 not really necessary but would be cleaner */
158                                         strncpy(user_name,temp_val, length);
159                                 }
160                         }
161                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
162                         temp_val = strchr(line_buf+i,'=');
163                         if(temp_val) {
164                                 /* go past equals sign */
165                                 temp_val++;
166                                 for(length = 0;length<65;length++) {
167                                         if(temp_val[length] == '\n')
168                                                 break;
169                                 }
170                                 if(length > 64) {
171                                         printf("mount.cifs failed: password in credentials file too long\n");
172                                         memset(line_buf,0, 4096);
173                                         if(mountpassword) {
174                                                 memset(mountpassword,0,64);
175                                         }
176                                         exit(1);
177                                 } else {
178                                         if(mountpassword == NULL) {
179                                                 mountpassword = calloc(65,1);
180                                         } else
181                                                 memset(mountpassword,0,64);
182                                         if(mountpassword) {
183                                                 /* BB add handling for commas in password here */
184                                                 strncpy(mountpassword,temp_val,length);
185                                                 got_password = 1;
186                                         }
187                                 }
188                         }
189                 }
190         }
191         fclose(fs);
192         if(line_buf) {
193                 memset(line_buf,0,4096);
194                 free(line_buf);
195         }
196         return 0;
197 }
198
199 static int get_password_from_file(int file_descript, char * filename)
200 {
201         int rc = 0;
202         int i;
203         char c;
204
205         if(mountpassword == NULL)
206                 mountpassword = calloc(65,1);
207         else
208                 memset(mountpassword, 0, 64);
209
210         if(filename != NULL) {
211                 file_descript = open(filename, O_RDONLY);
212                 if(file_descript < 0) {
213                         printf("mount.cifs failed. %s attempting to open password file %s\n",
214                                    strerror(errno),filename);
215                         exit(1);
216                 }
217         }
218         /* else file already open and fd provided */
219
220         for(i=0;i<64;i++) {
221                 rc = read(file_descript,&c,1);
222                 if(rc < 0) {
223                         printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
224                         memset(mountpassword,0,64);
225                         if(filename != NULL)
226                                 close(file_descript);
227                         exit(1);
228                 } else if(rc == 0) {
229                         if(mountpassword[0] == 0) {
230                                 if(verboseflag)
231                                         printf("\nWarning: null password used since cifs password file empty");
232                         }
233                         break;
234                 } else /* read valid character */ {
235                         if((c == 0) || (c == '\n')) {
236                                 break;
237                         } else
238                                 mountpassword[i] = c;
239                 }
240         }
241         if((i == 64) && (verboseflag)) {
242                 printf("\nWarning: password longer than 64 characters specified in cifs password file");
243         }
244         got_password = 1;
245         if(filename != NULL) {
246                 close(file_descript);
247         }
248
249         return rc;
250 }
251
252 static int parse_options(char * options, int * filesys_flags)
253 {
254         char * data;
255         char * percent_char = NULL;
256         char * value = NULL;
257         char * next_keyword = NULL;
258         int rc = 0;
259
260         if (!options)
261                 return 1;
262         else
263                 data = options;
264
265         if(verboseflag)
266                 printf("\n parsing options: %s", options);
267
268 /* while ((data = strsep(&options, ",")) != NULL) { */
269         while(data != NULL) {
270                 /*  check if ends with trailing comma */
271                 if(*data == 0)
272                         break;
273
274                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
275                 /* data  = next keyword */
276                 /* value = next value ie stuff after equal sign */
277
278                 next_keyword = strchr(data,',');
279        
280                 /* temporarily null terminate end of keyword=value pair */
281                 if(next_keyword)
282                         *next_keyword = 0;
283
284                 /* if (!*data)
285                         continue; */
286                
287                 /* temporarily null terminate keyword to make keyword and value distinct */
288                 if ((value = strchr(data, '=')) != NULL) {
289                         *value = '\0';
290                         value++;
291                 }
292
293                 if (strncmp(data, "user", 4) == 0) {
294                         if (!value || !*value) {
295                                 if(data[4] == '\0') {
296                                         if(verboseflag)
297                                                 printf("\nskipping empty user mount parameter\n");
298                                         /* remove the parm since it would otherwise be confusing
299                                         to the kernel code which would think it was a real username */
300                                                 data[0] = ',';
301                                                 data[1] = ',';
302                                                 data[2] = ',';
303                                                 data[3] = ',';
304                                         /* BB remove it from mount line so as not to confuse kernel code */
305                                 } else {
306                                         printf("username specified with no parameter\n");
307                                         return 1;       /* needs_arg; */
308                                 }
309                         } else {
310                                 if (strnlen(value, 260) < 260) {
311                                         got_user=1;
312                                         percent_char = strchr(value,'%');
313                                         if(percent_char) {
314                                                 *percent_char = ',';
315                                                 if(mountpassword == NULL)
316                                                         mountpassword = calloc(65,1);
317                                                 if(mountpassword) {
318                                                         if(got_password)
319                                                                 printf("\nmount.cifs warning - password specified twice\n");
320                                                         got_password = 1;
321                                                         percent_char++;
322                                                         strncpy(mountpassword, percent_char,64);
323                                                 /*  remove password from username */
324                                                         while(*percent_char != 0) {
325                                                                 *percent_char = ',';
326                                                                 percent_char++;
327                                                         }
328                                                 }
329                                         }
330                                 } else {
331                                         printf("username too long\n");
332                                         return 1;
333                                 }
334                         }
335                 } else if (strncmp(data, "pass", 4) == 0) {
336                         if (!value || !*value) {
337                                 if(got_password) {
338                                         printf("\npassword specified twice, ignoring second\n");
339                                 } else
340                                         got_password = 1;
341                         } else if (strnlen(value, 17) < 17) {
342                                 if(got_password)
343                                         printf("\nmount.cifs warning - password specified twice\n");
344                                 got_password = 1;
345                         } else {
346                                 printf("password too long\n");
347                                 return 1;
348                         }
349                 } else if (strncmp(data, "ip", 2) == 0) {
350                         if (!value || !*value) {
351                                 printf("target ip address argument missing");
352                         } else if (strnlen(value, 35) < 35) {
353                                 if(verboseflag)
354                                         printf("ip address %s override specified\n",value);
355                                 got_ip = 1;
356                         } else {
357                                 printf("ip address too long\n");
358                                 return 1;
359                         }
360                 } else if ((strncmp(data, "unc", 3) == 0)
361                    || (strncmp(data, "target", 6) == 0)
362                    || (strncmp(data, "path", 4) == 0)) {
363                         if (!value || !*value) {
364                                 printf("invalid path to network resource\n");
365                                 return 1;  /* needs_arg; */
366                         } else if(strnlen(value,5) < 5) {
367                                 printf("UNC name too short");
368                         }
369
370                         if (strnlen(value, 300) < 300) {
371                                 got_unc = 1;
372                                 if (strncmp(value, "//", 2) == 0) {
373                                         if(got_unc)
374                                                 printf("unc name specified twice, ignoring second\n");
375                                         else
376                                                 got_unc = 1;
377                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
378                                         printf("UNC Path does not begin with // or \\\\ \n");
379                                         return 1;
380                                 } else {
381                                         if(got_unc)
382                                                 printf("unc name specified twice, ignoring second\n");
383                                         else
384                                                 got_unc = 1;
385                                 }
386                         } else {
387                                 printf("CIFS: UNC name too long\n");
388                                 return 1;
389                         }
390                 } else if ((strncmp(data, "domain", 3) == 0)
391                            || (strncmp(data, "workgroup", 5) == 0)) {
392                         if (!value || !*value) {
393                                 printf("CIFS: invalid domain name\n");
394                                 return 1;       /* needs_arg; */
395                         }
396                         if (strnlen(value, 65) < 65) {
397                                 got_domain = 1;
398                         } else {
399                                 printf("domain name too long\n");
400                                 return 1;
401                         }
402                 } else if (strncmp(data, "cred", 4) == 0) {
403                         if (value && *value) {
404                                 rc = open_cred_file(value);
405                                 if(rc) {
406                                         printf("error %d opening credential file %s\n",rc, value);
407                                         return 1;
408                                 }
409                         } else {
410                                 printf("invalid credential file name specified\n");
411                                 return 1;
412                         }
413                 } else if (strncmp(data, "uid", 3) == 0) {
414                         if (value && *value) {
415                                 got_uid = 1;
416                         }
417                 } else if (strncmp(data, "gid", 3) == 0) {
418                         if (value && *value) {
419                                 got_gid = 1;
420                         }
421        /* fmask and dmask synonyms for people used to smbfs syntax */
422                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
423                         if (!value || !*value) {
424                                 printf ("Option '%s' requires a numerical argument\n", data);
425                                 return 1;
426                         }
427
428                         if (value[0] != '0') {
429                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
430                         }
431
432                         if (strcmp (data, "fmask") == 0) {
433                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
434                                 data = "file_mode"; /* BB fix this */
435                         }
436                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
437                         if (!value || !*value) {
438                                 printf ("Option '%s' requires a numerical argument\n", data);
439                                 return 1;
440                         }
441
442                         if (value[0] != '0') {
443                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
444                         }
445
446                         if (strcmp (data, "dmask") == 0) {
447                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
448                                 data = "dir_mode";
449                         }
450                         /* the following eight mount options should be
451                         stripped out from what is passed into the kernel
452                         since these eight options are best passed as the
453                         mount flags rather than redundantly to the kernel
454                         and could generate spurious warnings depending on the
455                         level of the corresponding cifs vfs kernel code */
456                 } else if (strncmp(data, "nosuid", 6) == 0) {
457                         *filesys_flags |= MS_NOSUID;
458                 } else if (strncmp(data, "suid", 4) == 0) {
459                         *filesys_flags &= ~MS_NOSUID;
460                 } else if (strncmp(data, "nodev", 5) == 0) {
461                         *filesys_flags |= MS_NODEV;
462                 } else if (strncmp(data, "dev", 3) == 0) {
463                         *filesys_flags &= ~MS_NODEV;
464                 } else if (strncmp(data, "noexec", 6) == 0) {
465                         *filesys_flags |= MS_NOEXEC;
466                 } else if (strncmp(data, "exec", 4) == 0) {
467                         *filesys_flags &= ~MS_NOEXEC;
468                 } else if (strncmp(data, "guest", 5) == 0) {
469                         got_password=1;
470                 } else if (strncmp(data, "ro", 2) == 0) {
471                         *filesys_flags |= MS_RDONLY;
472                 } else if (strncmp(data, "rw", 2) == 0) {
473                         *filesys_flags &= ~MS_RDONLY;
474                 } /* else if (strnicmp(data, "port", 4) == 0) {
475                         if (value && *value) {
476                                 vol->port =
477                                         simple_strtoul(value, &value, 0);
478                         }
479                 } else if (strnicmp(data, "rsize", 5) == 0) {
480                         if (value && *value) {
481                                 vol->rsize =
482                                         simple_strtoul(value, &value, 0);
483                         }
484                 } else if (strnicmp(data, "wsize", 5) == 0) {
485                         if (value && *value) {
486                                 vol->wsize =
487                                         simple_strtoul(value, &value, 0);
488                         }
489                 } else if (strnicmp(data, "version", 3) == 0) {
490                 } else {
491                         printf("CIFS: Unknown mount option %s\n",data);
492                 } */ /* nothing to do on those four mount options above.
493                         Just pass to kernel and ignore them here */
494
495                         /* move to next option */
496                 data = next_keyword+1;
497
498                 /* put overwritten equals sign back */
499                 if(value) {
500                         value--;
501                         *value = '=';
502                 }
503        
504                 /* put previous overwritten comma back */
505                 if(next_keyword)
506                         *next_keyword = ',';
507                 else
508                         data = NULL;
509         }
510         return 0;
511 }
512
513 /* Note that caller frees the returned buffer if necessary */
514 char * parse_server(char ** punc_name)
515 {
516         char * unc_name = *punc_name;
517         int length = strnlen(unc_name,1024);
518         char * share;
519         char * ipaddress_string = NULL;
520         struct hostent * host_entry;
521         struct in_addr server_ipaddr;
522         int rc;
523
524         if(length > 1023) {
525                 printf("mount error: UNC name too long");
526                 return NULL;
527         }
528         if (strncasecmp("cifs://",unc_name,7) == 0)
529                 return parse_cifs_url(unc_name+7);
530         if (strncasecmp("smb://",unc_name,6) == 0) {
531                 return parse_cifs_url(unc_name+6);
532         }
533
534         if(length < 3) {
535                 /* BB add code to find DFS root here */
536                 printf("\nMounting the DFS root for domain not implemented yet");
537                 return NULL;
538         } else {
539                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
540                         /* check for nfs syntax ie server:share */
541                         share = strchr(unc_name,':');
542                         if(share) {
543                                 free_share_name = 1;
544                                 *punc_name = malloc(length+3);
545                                 *share = '/';
546                                 strncpy((*punc_name)+2,unc_name,length);
547                                 unc_name = *punc_name;
548                                 unc_name[length+2] = 0;
549                                 goto continue_unc_parsing;
550                         } else {
551                                 printf("mount error: improperly formatted UNC name.");
552                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
553                                 return NULL;
554                         }
555                 } else {
556 continue_unc_parsing:
557                         unc_name[0] = '/';
558                         unc_name[1] = '/';
559                         unc_name += 2;
560                         if ((share = strchr(unc_name, '/')) ||
561                                 (share = strchr(unc_name,'\\'))) {
562                                 *share = 0;  /* temporarily terminate the string */
563                                 share += 1;
564                                 if(got_ip == 0) {
565                                         host_entry = gethostbyname(unc_name);
566                                 }
567                                 *(share - 1) = '/'; /* put the slash back */
568                                 if(got_ip) {
569                                         if(verboseflag)
570                                                 printf("ip address specified explicitly\n");
571                                         return NULL;
572                                 }
573                                 if(host_entry == NULL) {
574                                         printf("mount error: could not find target server. TCP name %s not found ", unc_name);
575                                         printf(" rc = %d\n",rc);
576                                         return NULL;
577                                 } else {
578                                         /* BB should we pass an alternate version of the share name as Unicode */
579                                         /* BB what about ipv6? BB */
580                                         /* BB add retries with alternate servers in list */
581
582                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
583
584                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
585                                         if(ipaddress_string == NULL) {
586                                                 printf("mount error: could not get valid ip address for target server\n");
587                                                 return NULL;
588                                         }
589                                         return ipaddress_string;
590                                 }
591                         } else {
592                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
593                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
594                                 return NULL;
595                         }
596                 }
597         }
598 }
599
600 static struct option longopts[] = {
601         { "all", 0, NULL, 'a' },
602         { "help",0, NULL, 'h' },
603         { "move",0, NULL, 'm' },
604         { "bind",0, NULL, 'b' },
605         { "read-only", 0, NULL, 'r' },
606         { "ro", 0, NULL, 'r' },
607         { "verbose", 0, NULL, 'v' },
608         { "version", 0, NULL, 'V' },
609         { "read-write", 0, NULL, 'w' },
610         { "rw", 0, NULL, 'w' },
611         { "options", 1, NULL, 'o' },
612         { "type", 1, NULL, 't' },
613         { "rsize",1, NULL, 'R' },
614         { "wsize",1, NULL, 'W' },
615         { "uid", 1, NULL, '1'},
616         { "gid", 1, NULL, '2'},
617         { "user",1,NULL,'u'},
618         { "username",1,NULL,'u'},
619         { "dom",1,NULL,'d'},
620         { "domain",1,NULL,'d'},
621         { "password",1,NULL,'p'},
622         { "pass",1,NULL,'p'},
623         { "credentials",1,NULL,'c'},
624         { "port",1,NULL,'P'},
625         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
626         { NULL, 0, NULL, 0 }
627 };
628
629 int main(int argc, char ** argv)
630 {
631         int c;
632         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
633         char * orgoptions = NULL;
634         char * share_name = NULL;
635         char * domain_name = NULL;
636         char * ipaddr = NULL;
637         char * uuid = NULL;
638         char * mountpoint;
639         char * options;
640         char * resolved_path;
641         char * temp;
642         int rc;
643         int rsize = 0;
644         int wsize = 0;
645         int nomtab = 0;
646         int uid = 0;
647         int gid = 0;
648         int optlen = 0;
649         int orgoptlen = 0;
650         struct stat statbuf;
651         struct utsname sysinfo;
652         struct mntent mo