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

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

Update branch to 3.0.31 release

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