source: branches/samba-3.0/source/client/mount.cifs.c@ 124

Last change on this file since 124 was 124, checked in by Paul Smedley, 17 years ago

Update source to 3.0.28a

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