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

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

Update source to 3.0.29

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