Fix bug #8664 - Renaming a symlink fails if the symlink target is outside of the...
[Samba.git] / client / mount.cifs.c
blob1b472c2ea50c8dc4998355999ea0619d9b9a14b6
1 /*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
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 #include <fstab.h>
43 #include "mount.h"
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "14"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #ifdef _SAMBA_BUILD_
50 #include "version.h"
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53 #else
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56 #else
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
61 #ifdef _SAMBA_BUILD_
62 #include "include/config.h"
63 #endif
65 #ifndef MS_MOVE
66 #define MS_MOVE 8192
67 #endif
69 #ifndef MS_BIND
70 #define MS_BIND 4096
71 #endif
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS 0x40000000
75 #define MS_USER 0x80000000
77 #define MAX_UNC_LEN 1024
79 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
81 #ifndef SAFE_FREE
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
83 #endif
85 #define MOUNT_PASSWD_SIZE 128
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * mount.cifs has been the subject of many "security" bugs that have arisen
93 * because of users and distributions installing it as a setuid root program.
94 * mount.cifs has not been audited for security. Thus, we strongly recommend
95 * that it not be installed setuid root. To make that abundantly clear,
96 * mount.cifs now check whether it's running setuid root and exit with an
97 * error if it is. If you wish to disable this check, then set the following
98 * #define to 1, but please realize that you do so at your own peril.
100 #define CIFS_DISABLE_SETUID_CHECK 0
103 * By default, mount.cifs follows the conventions set forth by /bin/mount
104 * for user mounts. That is, it requires that the mount be listed in
105 * /etc/fstab with the "user" option when run as an unprivileged user and
106 * mount.cifs is setuid root.
108 * Older versions of mount.cifs however were "looser" in this regard. When
109 * made setuid root, a user could run mount.cifs directly and mount any share
110 * on a directory owned by that user.
112 * The legacy behavior is now disabled by default. To reenable it, set the
113 * following #define to true.
115 #define CIFS_LEGACY_SETUID_CHECK 0
118 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
119 * flags by default. These defaults can be changed here.
121 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
123 const char *thisprogram;
124 int verboseflag = 0;
125 int fakemnt = 0;
126 static int got_password = 0;
127 static int got_user = 0;
128 static int got_domain = 0;
129 static int got_ip = 0;
130 static int got_unc = 0;
131 static int got_uid = 0;
132 static int got_gid = 0;
133 static char * user_name = NULL;
134 static char * mountpassword = NULL;
135 char * domain_name = NULL;
136 char * prefixpath = NULL;
138 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
139 * don't link to libreplace so need them here. */
141 /* like strncpy but does not 0 fill the buffer and always null
142 * terminates. bufsize is the size of the destination buffer */
144 #ifndef HAVE_STRLCPY
145 static size_t strlcpy(char *d, const char *s, size_t bufsize)
147 size_t len = strlen(s);
148 size_t ret = len;
149 if (bufsize <= 0) return 0;
150 if (len >= bufsize) len = bufsize-1;
151 memcpy(d, s, len);
152 d[len] = 0;
153 return ret;
155 #endif
157 /* like strncat but does not 0 fill the buffer and always null
158 * terminates. bufsize is the length of the buffer, which should
159 * be one more than the maximum resulting string length */
161 #ifndef HAVE_STRLCAT
162 static size_t strlcat(char *d, const char *s, size_t bufsize)
164 size_t len1 = strlen(d);
165 size_t len2 = strlen(s);
166 size_t ret = len1 + len2;
168 if (len1+len2 >= bufsize) {
169 if (bufsize < (len1+1)) {
170 return ret;
172 len2 = bufsize - (len1+1);
174 if (len2 > 0) {
175 memcpy(d+len1, s, len2);
176 d[len1+len2] = 0;
178 return ret;
180 #endif
183 * If an unprivileged user is doing the mounting then we need to ensure
184 * that the entry is in /etc/fstab.
186 static int
187 check_mountpoint(const char *progname, char *mountpoint)
189 int err;
190 struct stat statbuf;
192 /* does mountpoint exist and is it a directory? */
193 err = stat(".", &statbuf);
194 if (err) {
195 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
196 mountpoint, strerror(errno));
197 return EX_USAGE;
200 if (!S_ISDIR(statbuf.st_mode)) {
201 fprintf(stderr, "%s: %s is not a directory!", progname,
202 mountpoint);
203 return EX_USAGE;
206 #if CIFS_LEGACY_SETUID_CHECK
207 /* do extra checks on mountpoint for legacy setuid behavior */
208 if (!getuid() || geteuid())
209 return 0;
211 if (statbuf.st_uid != getuid()) {
212 fprintf(stderr, "%s: %s is not owned by user\n", progname,
213 mountpoint);
214 return EX_USAGE;
217 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
218 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
219 mountpoint);
220 return EX_USAGE;
222 #endif /* CIFS_LEGACY_SETUID_CHECK */
224 return 0;
227 #if CIFS_DISABLE_SETUID_CHECK
228 static int
229 check_setuid(void)
231 return 0;
233 #else /* CIFS_DISABLE_SETUID_CHECK */
234 static int
235 check_setuid(void)
237 if (getuid() && !geteuid()) {
238 printf("This mount.cifs program has been built with the "
239 "ability to run as a setuid root program disabled.\n"
240 "mount.cifs has not been well audited for security "
241 "holes. Therefore the Samba team does not recommend "
242 "installing it as a setuid root program.\n");
243 return 1;
246 return 0;
248 #endif /* CIFS_DISABLE_SETUID_CHECK */
250 #if CIFS_LEGACY_SETUID_CHECK
251 static int
252 check_fstab(const char *progname, char *mountpoint, char *devname,
253 char **options)
255 return 0;
257 #else /* CIFS_LEGACY_SETUID_CHECK */
258 static int
259 check_fstab(const char *progname, char *mountpoint, char *devname,
260 char **options)
262 FILE *fstab;
263 struct mntent *mnt;
265 /* make sure this mount is listed in /etc/fstab */
266 fstab = setmntent(_PATH_FSTAB, "r");
267 if (!fstab) {
268 fprintf(stderr, "Couldn't open %s for reading!\n",
269 _PATH_FSTAB);
270 return EX_FILEIO;
273 while((mnt = getmntent(fstab))) {
274 if (!strcmp(mountpoint, mnt->mnt_dir))
275 break;
277 endmntent(fstab);
279 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
280 fprintf(stderr, "%s: permission denied: no match for "
281 "%s found in %s\n", progname, mountpoint,
282 _PATH_FSTAB);
283 return EX_USAGE;
287 * 'mount' munges the options from fstab before passing them
288 * to us. It is non-trivial to test that we have the correct
289 * set of options. We don't want to trust what the user
290 * gave us, so just take whatever is in /etc/fstab.
292 free(*options);
293 *options = strdup(mnt->mnt_opts);
294 return 0;
296 #endif /* CIFS_LEGACY_SETUID_CHECK */
298 /* BB finish BB
300 cifs_umount
301 open nofollow - avoid symlink exposure?
302 get owner of dir see if matches self or if root
303 call system(umount argv) etc.
305 BB end finish BB */
307 static char * check_for_domain(char **);
310 static void mount_cifs_usage(FILE *stream)
312 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
313 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
314 fprintf(stream, " to a local directory.\n\nOptions:\n");
315 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
316 fprintf(stream, "\nLess commonly used options:");
317 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
318 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
319 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
320 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
321 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
322 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
323 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
324 fprintf(stream, "\n\nRarely used options:");
325 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
326 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
327 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
328 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
329 fprintf(stream, "\n\tman 8 mount.cifs\n");
330 fprintf(stream, "\nTo display the version number of the mount helper:");
331 fprintf(stream, "\n\t%s -V\n",thisprogram);
333 SAFE_FREE(mountpassword);
335 if (stream == stderr)
336 exit(EX_USAGE);
337 exit(0);
340 /* caller frees username if necessary */
341 static char * getusername(void) {
342 char *username = NULL;
343 struct passwd *password = getpwuid(getuid());
345 if (password) {
346 username = password->pw_name;
348 return username;
351 static int open_cred_file(char * file_name)
353 char * line_buf;
354 char * temp_val;
355 FILE * fs;
356 int i, length;
358 i = access(file_name, R_OK);
359 if (i)
360 return i;
362 fs = fopen(file_name,"r");
363 if(fs == NULL)
364 return errno;
365 line_buf = (char *)malloc(4096);
366 if(line_buf == NULL) {
367 fclose(fs);
368 return ENOMEM;
371 while(fgets(line_buf,4096,fs)) {
372 /* parse line from credential file */
374 /* eat leading white space */
375 for(i=0;i<4086;i++) {
376 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
377 break;
378 /* if whitespace - skip past it */
380 if (strncasecmp("username",line_buf+i,8) == 0) {
381 temp_val = strchr(line_buf + i,'=');
382 if(temp_val) {
383 /* go past equals sign */
384 temp_val++;
385 for(length = 0;length<4087;length++) {
386 if ((temp_val[length] == '\n')
387 || (temp_val[length] == '\0')) {
388 temp_val[length] = '\0';
389 break;
392 if(length > 4086) {
393 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
394 memset(line_buf,0,4096);
395 exit(EX_USAGE);
396 } else {
397 got_user = 1;
398 user_name = (char *)calloc(1 + length,1);
399 /* BB adding free of user_name string before exit,
400 not really necessary but would be cleaner */
401 strlcpy(user_name,temp_val, length+1);
404 } else if (strncasecmp("password",line_buf+i,8) == 0) {
405 temp_val = strchr(line_buf+i,'=');
406 if(temp_val) {
407 /* go past equals sign */
408 temp_val++;
409 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
410 if ((temp_val[length] == '\n')
411 || (temp_val[length] == '\0')) {
412 temp_val[length] = '\0';
413 break;
416 if(length > MOUNT_PASSWD_SIZE) {
417 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
418 memset(line_buf,0, 4096);
419 exit(EX_USAGE);
420 } else {
421 if(mountpassword == NULL) {
422 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
423 } else
424 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
425 if(mountpassword) {
426 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
427 got_password = 1;
431 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
432 temp_val = strchr(line_buf+i,'=');
433 if(temp_val) {
434 /* go past equals sign */
435 temp_val++;
436 if(verboseflag)
437 fprintf(stderr, "\nDomain %s\n",temp_val);
438 for(length = 0;length<DOMAIN_SIZE+1;length++) {
439 if ((temp_val[length] == '\n')
440 || (temp_val[length] == '\0')) {
441 temp_val[length] = '\0';
442 break;
445 if(length > DOMAIN_SIZE) {
446 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
447 exit(EX_USAGE);
448 } else {
449 if(domain_name == NULL) {
450 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
451 } else
452 memset(domain_name,0,DOMAIN_SIZE);
453 if(domain_name) {
454 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
455 got_domain = 1;
462 fclose(fs);
463 SAFE_FREE(line_buf);
464 return 0;
467 static int get_password_from_file(int file_descript, char * filename)
469 int rc = 0;
470 int i;
471 char c;
473 if(mountpassword == NULL)
474 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
475 else
476 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
478 if (mountpassword == NULL) {
479 fprintf(stderr, "malloc failed\n");
480 exit(EX_SYSERR);
483 if(filename != NULL) {
484 rc = access(filename, R_OK);
485 if (rc) {
486 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
487 filename, strerror(errno));
488 exit(EX_SYSERR);
490 file_descript = open(filename, O_RDONLY);
491 if(file_descript < 0) {
492 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
493 strerror(errno),filename);
494 exit(EX_SYSERR);
497 /* else file already open and fd provided */
499 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
500 rc = read(file_descript,&c,1);
501 if(rc < 0) {
502 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
503 if(filename != NULL)
504 close(file_descript);
505 exit(EX_SYSERR);
506 } else if(rc == 0) {
507 if(mountpassword[0] == 0) {
508 if(verboseflag)
509 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
511 break;
512 } else /* read valid character */ {
513 if((c == 0) || (c == '\n')) {
514 mountpassword[i] = '\0';
515 break;
516 } else
517 mountpassword[i] = c;
520 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
521 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
522 MOUNT_PASSWD_SIZE);
524 got_password = 1;
525 if(filename != NULL) {
526 close(file_descript);
529 return rc;
532 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
534 const char * data;
535 char * percent_char = NULL;
536 char * value = NULL;
537 char * next_keyword = NULL;
538 char * out = NULL;
539 int out_len = 0;
540 int word_len;
541 int rc = 0;
542 char user[32];
543 char group[32];
545 if (!optionsp || !*optionsp)
546 return 1;
547 data = *optionsp;
549 /* BB fixme check for separator override BB */
551 if (getuid()) {
552 got_uid = 1;
553 snprintf(user,sizeof(user),"%u",getuid());
554 got_gid = 1;
555 snprintf(group,sizeof(group),"%u",getgid());
558 /* while ((data = strsep(&options, ",")) != NULL) { */
559 while(data != NULL) {
560 /* check if ends with trailing comma */
561 if(*data == 0)
562 break;
564 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
565 /* data = next keyword */
566 /* value = next value ie stuff after equal sign */
568 next_keyword = strchr(data,','); /* BB handle sep= */
570 /* temporarily null terminate end of keyword=value pair */
571 if(next_keyword)
572 *next_keyword++ = 0;
574 /* temporarily null terminate keyword to make keyword and value distinct */
575 if ((value = strchr(data, '=')) != NULL) {
576 *value = '\0';
577 value++;
580 if (strncmp(data, "users",5) == 0) {
581 if(!value || !*value) {
582 *filesys_flags |= MS_USERS;
583 goto nocopy;
585 } else if (strncmp(data, "user_xattr",10) == 0) {
586 /* do nothing - need to skip so not parsed as user name */
587 } else if (strncmp(data, "user", 4) == 0) {
589 if (!value || !*value) {
590 if(data[4] == '\0') {
591 *filesys_flags |= MS_USER;
592 goto nocopy;
593 } else {
594 fprintf(stderr, "username specified with no parameter\n");
595 SAFE_FREE(out);
596 return 1; /* needs_arg; */
598 } else {
599 if (strnlen(value, 260) < 260) {
600 got_user=1;
601 percent_char = strchr(value,'%');
602 if(percent_char) {
603 *percent_char = ',';
604 if(mountpassword == NULL)
605 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
606 if(mountpassword) {
607 if(got_password)
608 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
609 got_password = 1;
610 percent_char++;
611 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
612 /* remove password from username */
613 while(*percent_char != 0) {
614 *percent_char = ',';
615 percent_char++;
619 /* this is only case in which the user
620 name buf is not malloc - so we have to
621 check for domain name embedded within
622 the user name here since the later
623 call to check_for_domain will not be
624 invoked */
625 domain_name = check_for_domain(&value);
626 } else {
627 fprintf(stderr, "username too long\n");
628 SAFE_FREE(out);
629 return 1;
632 } else if (strncmp(data, "pass", 4) == 0) {
633 if (!value || !*value) {
634 if(got_password) {
635 fprintf(stderr, "\npassword specified twice, ignoring second\n");
636 } else
637 got_password = 1;
638 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
639 if (got_password) {
640 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
641 } else {
642 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
643 if (!mountpassword) {
644 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
645 SAFE_FREE(out);
646 return 1;
648 got_password = 1;
650 } else {
651 fprintf(stderr, "password too long\n");
652 SAFE_FREE(out);
653 return 1;
655 goto nocopy;
656 } else if (strncmp(data, "sec", 3) == 0) {
657 if (value) {
658 if (!strncmp(value, "none", 4) ||
659 !strncmp(value, "krb5", 4))
660 got_password = 1;
662 } else if (strncmp(data, "ip", 2) == 0) {
663 if (!value || !*value) {
664 fprintf(stderr, "target ip address argument missing");
665 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
666 if(verboseflag)
667 fprintf(stderr, "ip address %s override specified\n",value);
668 got_ip = 1;
669 } else {
670 fprintf(stderr, "ip address too long\n");
671 SAFE_FREE(out);
672 return 1;
674 } else if ((strncmp(data, "unc", 3) == 0)
675 || (strncmp(data, "target", 6) == 0)
676 || (strncmp(data, "path", 4) == 0)) {
677 if (!value || !*value) {
678 fprintf(stderr, "invalid path to network resource\n");
679 SAFE_FREE(out);
680 return 1; /* needs_arg; */
681 } else if(strnlen(value,5) < 5) {
682 fprintf(stderr, "UNC name too short");
685 if (strnlen(value, 300) < 300) {
686 got_unc = 1;
687 if (strncmp(value, "//", 2) == 0) {
688 if(got_unc)
689 fprintf(stderr, "unc name specified twice, ignoring second\n");
690 else
691 got_unc = 1;
692 } else if (strncmp(value, "\\\\", 2) != 0) {
693 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
694 SAFE_FREE(out);
695 return 1;
696 } else {
697 if(got_unc)
698 fprintf(stderr, "unc name specified twice, ignoring second\n");
699 else
700 got_unc = 1;
702 } else {
703 fprintf(stderr, "CIFS: UNC name too long\n");
704 SAFE_FREE(out);
705 return 1;
707 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
708 || (strncmp(data, "workg", 5) == 0)) {
709 /* note this allows for synonyms of "domain"
710 such as "DOM" and "dom" and "workgroup"
711 and "WORKGRP" etc. */
712 if (!value || !*value) {
713 fprintf(stderr, "CIFS: invalid domain name\n");
714 SAFE_FREE(out);
715 return 1; /* needs_arg; */
717 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
718 got_domain = 1;
719 } else {
720 fprintf(stderr, "domain name too long\n");
721 SAFE_FREE(out);
722 return 1;
724 } else if (strncmp(data, "cred", 4) == 0) {
725 if (value && *value) {
726 rc = open_cred_file(value);
727 if(rc) {
728 fprintf(stderr, "error %d (%s) opening credential file %s\n",
729 rc, strerror(rc), value);
730 SAFE_FREE(out);
731 return 1;
733 } else {
734 fprintf(stderr, "invalid credential file name specified\n");
735 SAFE_FREE(out);
736 return 1;
738 } else if (strncmp(data, "uid", 3) == 0) {
739 if (value && *value) {
740 got_uid = 1;
741 if (!isdigit(*value)) {
742 struct passwd *pw;
744 if (!(pw = getpwnam(value))) {
745 fprintf(stderr, "bad user name \"%s\"\n", value);
746 exit(EX_USAGE);
748 snprintf(user, sizeof(user), "%u", pw->pw_uid);
749 } else {
750 strlcpy(user,value,sizeof(user));
753 goto nocopy;
754 } else if (strncmp(data, "gid", 3) == 0) {
755 if (value && *value) {
756 got_gid = 1;
757 if (!isdigit(*value)) {
758 struct group *gr;
760 if (!(gr = getgrnam(value))) {
761 fprintf(stderr, "bad group name \"%s\"\n", value);
762 exit(EX_USAGE);
764 snprintf(group, sizeof(group), "%u", gr->gr_gid);
765 } else {
766 strlcpy(group,value,sizeof(group));
769 goto nocopy;
770 /* fmask and dmask synonyms for people used to smbfs syntax */
771 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
772 if (!value || !*value) {
773 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
774 SAFE_FREE(out);
775 return 1;
778 if (value[0] != '0') {
779 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
782 if (strcmp (data, "fmask") == 0) {
783 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
784 data = "file_mode"; /* BB fix this */
786 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
787 if (!value || !*value) {
788 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
789 SAFE_FREE(out);
790 return 1;
793 if (value[0] != '0') {
794 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
797 if (strcmp (data, "dmask") == 0) {
798 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
799 data = "dir_mode";
801 /* the following eight mount options should be
802 stripped out from what is passed into the kernel
803 since these eight options are best passed as the
804 mount flags rather than redundantly to the kernel
805 and could generate spurious warnings depending on the
806 level of the corresponding cifs vfs kernel code */
807 } else if (strncmp(data, "nosuid", 6) == 0) {
808 *filesys_flags |= MS_NOSUID;
809 } else if (strncmp(data, "suid", 4) == 0) {
810 *filesys_flags &= ~MS_NOSUID;
811 } else if (strncmp(data, "nodev", 5) == 0) {
812 *filesys_flags |= MS_NODEV;
813 } else if ((strncmp(data, "nobrl", 5) == 0) ||
814 (strncmp(data, "nolock", 6) == 0)) {
815 *filesys_flags &= ~MS_MANDLOCK;
816 } else if (strncmp(data, "dev", 3) == 0) {
817 *filesys_flags &= ~MS_NODEV;
818 } else if (strncmp(data, "noexec", 6) == 0) {
819 *filesys_flags |= MS_NOEXEC;
820 } else if (strncmp(data, "exec", 4) == 0) {
821 *filesys_flags &= ~MS_NOEXEC;
822 } else if (strncmp(data, "guest", 5) == 0) {
823 user_name = (char *)calloc(1, 1);
824 got_user = 1;
825 got_password = 1;
826 } else if (strncmp(data, "ro", 2) == 0) {
827 *filesys_flags |= MS_RDONLY;
828 goto nocopy;
829 } else if (strncmp(data, "rw", 2) == 0) {
830 *filesys_flags &= ~MS_RDONLY;
831 goto nocopy;
832 } else if (strncmp(data, "remount", 7) == 0) {
833 *filesys_flags |= MS_REMOUNT;
834 } /* else if (strnicmp(data, "port", 4) == 0) {
835 if (value && *value) {
836 vol->port =
837 simple_strtoul(value, &value, 0);
839 } else if (strnicmp(data, "rsize", 5) == 0) {
840 if (value && *value) {
841 vol->rsize =
842 simple_strtoul(value, &value, 0);
844 } else if (strnicmp(data, "wsize", 5) == 0) {
845 if (value && *value) {
846 vol->wsize =
847 simple_strtoul(value, &value, 0);
849 } else if (strnicmp(data, "version", 3) == 0) {
850 } else {
851 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
852 } */ /* nothing to do on those four mount options above.
853 Just pass to kernel and ignore them here */
855 /* Copy (possibly modified) option to out */
856 word_len = strlen(data);
857 if (value)
858 word_len += 1 + strlen(value);
860 out = (char *)realloc(out, out_len + word_len + 2);
861 if (out == NULL) {
862 perror("malloc");
863 exit(EX_SYSERR);
866 if (out_len) {
867 strlcat(out, ",", out_len + word_len + 2);
868 out_len++;
871 if (value)
872 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
873 else
874 snprintf(out + out_len, word_len + 1, "%s", data);
875 out_len = strlen(out);
877 nocopy:
878 data = next_keyword;
881 /* special-case the uid and gid */
882 if (got_uid) {
883 word_len = strlen(user);
885 out = (char *)realloc(out, out_len + word_len + 6);
886 if (out == NULL) {
887 perror("malloc");
888 exit(EX_SYSERR);
891 if (out_len) {
892 strlcat(out, ",", out_len + word_len + 6);
893 out_len++;
895 snprintf(out + out_len, word_len + 5, "uid=%s", user);
896 out_len = strlen(out);
898 if (got_gid) {
899 word_len = strlen(group);
901 out = (char *)realloc(out, out_len + 1 + word_len + 6);
902 if (out == NULL) {
903 perror("malloc");
904 exit(EX_SYSERR);
907 if (out_len) {
908 strlcat(out, ",", out_len + word_len + 6);
909 out_len++;
911 snprintf(out + out_len, word_len + 5, "gid=%s", group);
912 out_len = strlen(out);
915 SAFE_FREE(*optionsp);
916 *optionsp = out;
917 return 0;
920 /* replace all (one or more) commas with double commas */
921 static void check_for_comma(char ** ppasswrd)
923 char *new_pass_buf;
924 char *pass;
925 int i,j;
926 int number_of_commas = 0;
927 int len;
929 if(ppasswrd == NULL)
930 return;
931 else
932 (pass = *ppasswrd);
934 len = strlen(pass);
936 for(i=0;i<len;i++) {
937 if(pass[i] == ',')
938 number_of_commas++;
941 if(number_of_commas == 0)
942 return;
943 if(number_of_commas > MOUNT_PASSWD_SIZE) {
944 /* would otherwise overflow the mount options buffer */
945 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
946 return;
949 new_pass_buf = (char *)malloc(len+number_of_commas+1);
950 if(new_pass_buf == NULL)
951 return;
953 for(i=0,j=0;i<len;i++,j++) {
954 new_pass_buf[j] = pass[i];
955 if(pass[i] == ',') {
956 j++;
957 new_pass_buf[j] = pass[i];
960 new_pass_buf[len+number_of_commas] = 0;
962 SAFE_FREE(*ppasswrd);
963 *ppasswrd = new_pass_buf;
965 return;
968 /* Usernames can not have backslash in them and we use
969 [BB check if usernames can have forward slash in them BB]
970 backslash as domain\user separator character
972 static char * check_for_domain(char **ppuser)
974 char * original_string;
975 char * usernm;
976 char * domainnm;
977 int original_len;
978 int len;
979 int i;
981 if(ppuser == NULL)
982 return NULL;
984 original_string = *ppuser;
986 if (original_string == NULL)
987 return NULL;
989 original_len = strlen(original_string);
991 usernm = strchr(*ppuser,'/');
992 if (usernm == NULL) {
993 usernm = strchr(*ppuser,'\\');
994 if (usernm == NULL)
995 return NULL;
998 if(got_domain) {
999 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
1000 return NULL;
1003 usernm[0] = 0;
1004 domainnm = *ppuser;
1005 if (domainnm[0] != 0) {
1006 got_domain = 1;
1007 } else {
1008 fprintf(stderr, "null domain\n");
1010 len = strlen(domainnm);
1011 /* reset domainm to new buffer, and copy
1012 domain name into it */
1013 domainnm = (char *)malloc(len+1);
1014 if(domainnm == NULL)
1015 return NULL;
1017 strlcpy(domainnm,*ppuser,len+1);
1019 /* move_string(*ppuser, usernm+1) */
1020 len = strlen(usernm+1);
1022 if(len >= original_len) {
1023 /* should not happen */
1024 return domainnm;
1027 for(i=0;i<original_len;i++) {
1028 if(i<len)
1029 original_string[i] = usernm[i+1];
1030 else /* stuff with commas to remove last parm */
1031 original_string[i] = ',';
1034 /* BB add check for more than one slash?
1035 strchr(*ppuser,'/');
1036 strchr(*ppuser,'\\')
1039 return domainnm;
1042 /* replace all occurances of "from" in a string with "to" */
1043 static void replace_char(char *string, char from, char to, int maxlen)
1045 char *lastchar = string + maxlen;
1046 while (string) {
1047 string = strchr(string, from);
1048 if (string) {
1049 *string = to;
1050 if (string >= lastchar)
1051 return;
1056 /* Note that caller frees the returned buffer if necessary */
1057 static struct addrinfo *
1058 parse_server(char ** punc_name)
1060 char * unc_name = *punc_name;
1061 int length = strnlen(unc_name, MAX_UNC_LEN);
1062 char * share;
1063 struct addrinfo *addrlist;
1064 int rc;
1066 if(length > (MAX_UNC_LEN - 1)) {
1067 fprintf(stderr, "mount error: UNC name too long");
1068 return NULL;
1070 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1071 (strncasecmp("smb://", unc_name, 6) == 0)) {
1072 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1073 return NULL;
1076 if(length < 3) {
1077 /* BB add code to find DFS root here */
1078 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1079 return NULL;
1080 } else {
1081 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1082 /* check for nfs syntax ie server:share */
1083 share = strchr(unc_name,':');
1084 if(share) {
1085 *punc_name = (char *)malloc(length+3);
1086 if(*punc_name == NULL) {
1087 /* put the original string back if
1088 no memory left */
1089 *punc_name = unc_name;
1090 return NULL;
1092 *share = '/';
1093 strlcpy((*punc_name)+2,unc_name,length+1);
1094 SAFE_FREE(unc_name);
1095 unc_name = *punc_name;
1096 unc_name[length+2] = 0;
1097 goto continue_unc_parsing;
1098 } else {
1099 fprintf(stderr, "mount error: improperly formatted UNC name.");
1100 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1101 return NULL;
1103 } else {
1104 continue_unc_parsing:
1105 unc_name[0] = '/';
1106 unc_name[1] = '/';
1107 unc_name += 2;
1109 /* allow for either delimiter between host and sharename */
1110 if ((share = strpbrk(unc_name, "/\\"))) {
1111 *share = 0; /* temporarily terminate the string */
1112 share += 1;
1113 if(got_ip == 0) {
1114 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1115 if (rc != 0) {
1116 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1117 unc_name, gai_strerror(rc));
1118 addrlist = NULL;
1121 *(share - 1) = '/'; /* put delimiter back */
1123 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1124 if ((prefixpath = strpbrk(share, "/\\"))) {
1125 *prefixpath = 0; /* permanently terminate the string */
1126 if (!strlen(++prefixpath))
1127 prefixpath = NULL; /* this needs to be done explicitly */
1129 if(got_ip) {
1130 if(verboseflag)
1131 fprintf(stderr, "ip address specified explicitly\n");
1132 return NULL;
1134 /* BB should we pass an alternate version of the share name as Unicode */
1136 return addrlist;
1137 } else {
1138 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1139 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1140 return NULL;
1146 static struct option longopts[] = {
1147 { "all", 0, NULL, 'a' },
1148 { "help",0, NULL, 'h' },
1149 { "move",0, NULL, 'm' },
1150 { "bind",0, NULL, 'b' },
1151 { "read-only", 0, NULL, 'r' },
1152 { "ro", 0, NULL, 'r' },
1153 { "verbose", 0, NULL, 'v' },
1154 { "version", 0, NULL, 'V' },
1155 { "read-write", 0, NULL, 'w' },
1156 { "rw", 0, NULL, 'w' },
1157 { "options", 1, NULL, 'o' },
1158 { "type", 1, NULL, 't' },
1159 { "rsize",1, NULL, 'R' },
1160 { "wsize",1, NULL, 'W' },
1161 { "uid", 1, NULL, '1'},
1162 { "gid", 1, NULL, '2'},
1163 { "user",1,NULL,'u'},
1164 { "username",1,NULL,'u'},
1165 { "dom",1,NULL,'d'},
1166 { "domain",1,NULL,'d'},
1167 { "password",1,NULL,'p'},
1168 { "pass",1,NULL,'p'},
1169 { "credentials",1,NULL,'c'},
1170 { "port",1,NULL,'P'},
1171 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1172 { NULL, 0, NULL, 0 }
1175 /* convert a string to uppercase. return false if the string
1176 * wasn't ASCII. Return success on a NULL ptr */
1177 static int
1178 uppercase_string(char *string)
1180 if (!string)
1181 return 1;
1183 while (*string) {
1184 /* check for unicode */
1185 if ((unsigned char) string[0] & 0x80)
1186 return 0;
1187 *string = toupper((unsigned char) *string);
1188 string++;
1191 return 1;
1194 static void print_cifs_mount_version(void)
1196 printf("mount.cifs version: %s.%s%s\n",
1197 MOUNT_CIFS_VERSION_MAJOR,
1198 MOUNT_CIFS_VERSION_MINOR,
1199 MOUNT_CIFS_VENDOR_SUFFIX);
1203 * This function borrowed from fuse-utils...
1205 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1206 * newlines embedded within the text fields. To make sure no one corrupts
1207 * the mtab, fail the mount if there are embedded newlines.
1209 static int check_newline(const char *progname, const char *name)
1211 char *s;
1212 for (s = "\n"; *s; s++) {
1213 if (strchr(name, *s)) {
1214 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1215 progname, *s);
1216 return EX_USAGE;
1219 return 0;
1222 static int check_mtab(const char *progname, const char *devname,
1223 const char *dir)
1225 if (check_newline(progname, devname) == -1 ||
1226 check_newline(progname, dir) == -1)
1227 return EX_USAGE;
1228 return 0;
1232 int main(int argc, char ** argv)
1234 int c;
1235 unsigned long flags = MS_MANDLOCK;
1236 char * orgoptions = NULL;
1237 char * share_name = NULL;
1238 const char * ipaddr = NULL;
1239 char * uuid = NULL;
1240 char * mountpoint = NULL;
1241 char * options = NULL;
1242 char * optionstail;
1243 char * resolved_path = NULL;
1244 char * temp;
1245 char * dev_name;
1246 int rc = 0;
1247 int rsize = 0;
1248 int wsize = 0;
1249 int nomtab = 0;
1250 int uid = 0;
1251 int gid = 0;
1252 int optlen = 0;
1253 int orgoptlen = 0;
1254 size_t options_size = 0;
1255 size_t current_len;
1256 int retry = 0; /* set when we have to retry mount with uppercase */
1257 struct addrinfo *addrhead = NULL, *addr;
1258 struct utsname sysinfo;
1259 struct mntent mountent;
1260 struct sockaddr_in *addr4;
1261 struct sockaddr_in6 *addr6;
1262 FILE * pmntfile;
1264 if (check_setuid())
1265 return EX_USAGE;
1267 /* setlocale(LC_ALL, "");
1268 bindtextdomain(PACKAGE, LOCALEDIR);
1269 textdomain(PACKAGE); */
1271 if(argc && argv)
1272 thisprogram = argv[0];
1273 else
1274 mount_cifs_usage(stderr);
1276 if(thisprogram == NULL)
1277 thisprogram = "mount.cifs";
1279 uname(&sysinfo);
1280 /* BB add workstation name and domain and pass down */
1282 /* #ifdef _GNU_SOURCE
1283 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1284 #endif */
1285 if(argc > 2) {
1286 dev_name = argv[1];
1287 share_name = strndup(argv[1], MAX_UNC_LEN);
1288 if (share_name == NULL) {
1289 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1290 exit(EX_SYSERR);
1292 mountpoint = argv[2];
1293 } else if (argc == 2) {
1294 if ((strcmp(argv[1], "-V") == 0) ||
1295 (strcmp(argv[1], "--version") == 0))
1297 print_cifs_mount_version();
1298 exit(0);
1301 if ((strcmp(argv[1], "-h") == 0) ||
1302 (strcmp(argv[1], "-?") == 0) ||
1303 (strcmp(argv[1], "--help") == 0))
1304 mount_cifs_usage(stdout);
1306 mount_cifs_usage(stderr);
1307 } else {
1308 mount_cifs_usage(stderr);
1312 /* add sharename in opts string as unc= parm */
1313 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1314 longopts, NULL)) != -1) {
1315 switch (c) {
1316 /* No code to do the following options yet */
1317 /* case 'l':
1318 list_with_volumelabel = 1;
1319 break;
1320 case 'L':
1321 volumelabel = optarg;
1322 break; */
1323 /* case 'a':
1324 ++mount_all;
1325 break; */
1327 case '?':
1328 case 'h': /* help */
1329 mount_cifs_usage(stdout);
1330 case 'n':
1331 ++nomtab;
1332 break;
1333 case 'b':
1334 #ifdef MS_BIND
1335 flags |= MS_BIND;
1336 #else
1337 fprintf(stderr,
1338 "option 'b' (MS_BIND) not supported\n");
1339 #endif
1340 break;
1341 case 'm':
1342 #ifdef MS_MOVE
1343 flags |= MS_MOVE;
1344 #else
1345 fprintf(stderr,
1346 "option 'm' (MS_MOVE) not supported\n");
1347 #endif
1348 break;
1349 case 'o':
1350 orgoptions = strdup(optarg);
1351 break;
1352 case 'r': /* mount readonly */
1353 flags |= MS_RDONLY;
1354 break;
1355 case 'U':
1356 uuid = optarg;
1357 break;
1358 case 'v':
1359 ++verboseflag;
1360 break;
1361 case 'V':
1362 print_cifs_mount_version();
1363 exit (0);
1364 case 'w':
1365 flags &= ~MS_RDONLY;
1366 break;
1367 case 'R':
1368 rsize = atoi(optarg) ;
1369 break;
1370 case 'W':
1371 wsize = atoi(optarg);
1372 break;
1373 case '1':
1374 if (isdigit(*optarg)) {
1375 char *ep;
1377 uid = strtoul(optarg, &ep, 10);
1378 if (*ep) {
1379 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1380 exit(EX_USAGE);
1382 } else {
1383 struct passwd *pw;
1385 if (!(pw = getpwnam(optarg))) {
1386 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1387 exit(EX_USAGE);
1389 uid = pw->pw_uid;
1390 endpwent();
1392 break;
1393 case '2':
1394 if (isdigit(*optarg)) {
1395 char *ep;
1397 gid = strtoul(optarg, &ep, 10);
1398 if (*ep) {
1399 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1400 exit(EX_USAGE);
1402 } else {
1403 struct group *gr;
1405 if (!(gr = getgrnam(optarg))) {
1406 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1407 exit(EX_USAGE);
1409 gid = gr->gr_gid;
1410 endpwent();
1412 break;
1413 case 'u':
1414 got_user = 1;
1415 user_name = optarg;
1416 break;
1417 case 'd':
1418 domain_name = optarg; /* BB fix this - currently ignored */
1419 got_domain = 1;
1420 break;
1421 case 'p':
1422 if(mountpassword == NULL)
1423 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1424 if(mountpassword) {
1425 got_password = 1;
1426 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1428 break;
1429 case 'S':
1430 get_password_from_file(0 /* stdin */,NULL);
1431 break;
1432 case 't':
1433 break;
1434 case 'f':
1435 ++fakemnt;
1436 break;
1437 default:
1438 fprintf(stderr, "unknown mount option %c\n",c);
1439 mount_cifs_usage(stderr);
1443 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1444 mount_cifs_usage(stderr);
1447 /* make sure mountpoint is legit */
1448 rc = chdir(mountpoint);
1449 if (rc) {
1450 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1451 strerror(errno));
1452 rc = EX_USAGE;
1453 goto mount_exit;
1456 rc = check_mountpoint(thisprogram, mountpoint);
1457 if (rc)
1458 goto mount_exit;
1460 /* sanity check for unprivileged mounts */
1461 if (getuid()) {
1462 rc = check_fstab(thisprogram, mountpoint, dev_name,
1463 &orgoptions);
1464 if (rc)
1465 goto mount_exit;
1467 /* enable any default user mount flags */
1468 flags |= CIFS_SETUID_FLAGS;
1471 if (getenv("PASSWD")) {
1472 if(mountpassword == NULL)
1473 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1474 if(mountpassword) {
1475 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1476 got_password = 1;
1478 } else if (getenv("PASSWD_FD")) {
1479 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1480 } else if (getenv("PASSWD_FILE")) {
1481 get_password_from_file(0, getenv("PASSWD_FILE"));
1484 if (orgoptions && parse_options(&orgoptions, &flags)) {
1485 rc = EX_USAGE;
1486 goto mount_exit;
1489 if (getuid()) {
1490 #if !CIFS_LEGACY_SETUID_CHECK
1491 if (!(flags & (MS_USERS|MS_USER))) {
1492 fprintf(stderr, "%s: permission denied\n", thisprogram);
1493 rc = EX_USAGE;
1494 goto mount_exit;
1496 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1498 if (geteuid()) {
1499 fprintf(stderr, "%s: not installed setuid - \"user\" "
1500 "CIFS mounts not supported.",
1501 thisprogram);
1502 rc = EX_FAIL;
1503 goto mount_exit;
1507 flags &= ~(MS_USERS|MS_USER);
1509 addrhead = addr = parse_server(&share_name);
1510 if((addrhead == NULL) && (got_ip == 0)) {
1511 fprintf(stderr, "No ip address specified and hostname not found\n");
1512 rc = EX_USAGE;
1513 goto mount_exit;
1516 /* BB save off path and pop after mount returns? */
1517 resolved_path = (char *)malloc(PATH_MAX+1);
1518 if (!resolved_path) {
1519 fprintf(stderr, "Unable to allocate memory.\n");
1520 rc = EX_SYSERR;
1521 goto mount_exit;
1524 /* Note that if we can not canonicalize the name, we get
1525 another chance to see if it is valid when we chdir to it */
1526 if(!realpath(".", resolved_path)) {
1527 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1528 mountpoint, strerror(errno));
1529 rc = EX_SYSERR;
1530 goto mount_exit;
1533 mountpoint = resolved_path;
1535 if(got_user == 0) {
1536 /* Note that the password will not be retrieved from the
1537 USER env variable (ie user%password form) as there is
1538 already a PASSWD environment varaible */
1539 if (getenv("USER"))
1540 user_name = strdup(getenv("USER"));
1541 if (user_name == NULL)
1542 user_name = getusername();
1543 got_user = 1;
1546 if(got_password == 0) {
1547 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1548 no good replacement yet. */
1549 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1550 if (!tmp_pass || !mountpassword) {
1551 fprintf(stderr, "Password not entered, exiting\n");
1552 exit(EX_USAGE);
1554 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1555 got_password = 1;
1557 /* FIXME launch daemon (handles dfs name resolution and credential change)
1558 remember to clear parms and overwrite password field before launching */
1559 if(orgoptions) {
1560 optlen = strlen(orgoptions);
1561 orgoptlen = optlen;
1562 } else
1563 optlen = 0;
1564 if(share_name)
1565 optlen += strlen(share_name) + 4;
1566 else {
1567 fprintf(stderr, "No server share name specified\n");
1568 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1569 exit(EX_USAGE);
1571 if(user_name)
1572 optlen += strlen(user_name) + 6;
1573 optlen += MAX_ADDRESS_LEN + 4;
1574 if(mountpassword)
1575 optlen += strlen(mountpassword) + 6;
1576 mount_retry:
1577 SAFE_FREE(options);
1578 options_size = optlen + 10 + DOMAIN_SIZE;
1579 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 */);
1581 if(options == NULL) {
1582 fprintf(stderr, "Could not allocate memory for mount options\n");
1583 exit(EX_SYSERR);
1586 strlcpy(options, "unc=", options_size);
1587 strlcat(options,share_name,options_size);
1588 /* scan backwards and reverse direction of slash */
1589 temp = strrchr(options, '/');
1590 if(temp > options + 6)
1591 *temp = '\\';
1592 if(user_name) {
1593 /* check for syntax like user=domain\user */
1594 if(got_domain == 0)
1595 domain_name = check_for_domain(&user_name);
1596 strlcat(options,",user=",options_size);
1597 strlcat(options,user_name,options_size);
1599 if(retry == 0) {
1600 if(domain_name) {
1601 /* extra length accounted for in option string above */
1602 strlcat(options,",domain=",options_size);
1603 strlcat(options,domain_name,options_size);
1607 strlcat(options,",ver=",options_size);
1608 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1610 if(orgoptions) {
1611 strlcat(options,",",options_size);
1612 strlcat(options,orgoptions,options_size);
1614 if(prefixpath) {
1615 strlcat(options,",prefixpath=",options_size);
1616 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1619 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1620 replace_char(dev_name, '\\', '/', strlen(share_name));
1622 if (!got_ip && addr) {
1623 strlcat(options, ",ip=", options_size);
1624 current_len = strnlen(options, options_size);
1625 optionstail = options + current_len;
1626 switch (addr->ai_addr->sa_family) {
1627 case AF_INET6:
1628 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1629 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1630 options_size - current_len);
1631 break;
1632 case AF_INET:
1633 addr4 = (struct sockaddr_in *) addr->ai_addr;
1634 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1635 options_size - current_len);
1636 break;
1637 default:
1638 ipaddr = NULL;
1641 /* if the address looks bogus, try the next one */
1642 if (!ipaddr) {
1643 addr = addr->ai_next;
1644 if (addr)
1645 goto mount_retry;
1646 rc = EX_SYSERR;
1647 goto mount_exit;
1651 if (addr && addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1652 strlcat(options, "%", options_size);
1653 current_len = strnlen(options, options_size);
1654 optionstail = options + current_len;
1655 snprintf(optionstail, options_size - current_len, "%u",
1656 addr6->sin6_scope_id);
1659 if(verboseflag)
1660 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1662 if (mountpassword) {
1664 * Commas have to be doubled, or else they will
1665 * look like the parameter separator
1667 if(retry == 0)
1668 check_for_comma(&mountpassword);
1669 strlcat(options,",pass=",options_size);
1670 strlcat(options,mountpassword,options_size);
1671 if (verboseflag)
1672 fprintf(stderr, ",pass=********");
1675 if (verboseflag)
1676 fprintf(stderr, "\n");
1678 rc = check_mtab(thisprogram, dev_name, mountpoint);
1679 if (rc)
1680 goto mount_exit;
1682 if (!fakemnt && mount(dev_name, ".", "cifs", flags, options)) {
1683 switch (errno) {
1684 case ECONNREFUSED:
1685 case EHOSTUNREACH:
1686 if (addr) {
1687 addr = addr->ai_next;
1688 if (addr)
1689 goto mount_retry;
1691 break;
1692 case ENODEV:
1693 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1694 break;
1695 case ENXIO:
1696 if(retry == 0) {
1697 retry = 1;
1698 if (uppercase_string(dev_name) &&
1699 uppercase_string(share_name) &&
1700 uppercase_string(prefixpath)) {
1701 fprintf(stderr, "retrying with upper case share name\n");
1702 goto mount_retry;
1706 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1707 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1708 "mount.cifs)\n");
1709 rc = EX_FAIL;
1710 goto mount_exit;
1713 if (nomtab)
1714 goto mount_exit;
1715 atexit(unlock_mtab);
1716 rc = lock_mtab();
1717 if (rc) {
1718 fprintf(stderr, "cannot lock mtab");
1719 goto mount_exit;
1721 pmntfile = setmntent(MOUNTED, "a+");
1722 if (!pmntfile) {
1723 fprintf(stderr, "could not update mount table\n");
1724 unlock_mtab();
1725 rc = EX_FILEIO;
1726 goto mount_exit;
1728 mountent.mnt_fsname = dev_name;
1729 mountent.mnt_dir = mountpoint;
1730 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1731 mountent.mnt_opts = (char *)malloc(220);
1732 if(mountent.mnt_opts) {
1733 char * mount_user = getusername();
1734 memset(mountent.mnt_opts,0,200);
1735 if(flags & MS_RDONLY)
1736 strlcat(mountent.mnt_opts,"ro",220);
1737 else
1738 strlcat(mountent.mnt_opts,"rw",220);
1739 if(flags & MS_MANDLOCK)
1740 strlcat(mountent.mnt_opts,",mand",220);
1741 if(flags & MS_NOEXEC)
1742 strlcat(mountent.mnt_opts,",noexec",220);
1743 if(flags & MS_NOSUID)
1744 strlcat(mountent.mnt_opts,",nosuid",220);
1745 if(flags & MS_NODEV)
1746 strlcat(mountent.mnt_opts,",nodev",220);
1747 if(flags & MS_SYNCHRONOUS)
1748 strlcat(mountent.mnt_opts,",sync",220);
1749 if(mount_user) {
1750 if(getuid() != 0) {
1751 strlcat(mountent.mnt_opts,
1752 ",user=", 220);
1753 strlcat(mountent.mnt_opts,
1754 mount_user, 220);
1758 mountent.mnt_freq = 0;
1759 mountent.mnt_passno = 0;
1760 rc = addmntent(pmntfile,&mountent);
1761 endmntent(pmntfile);
1762 unlock_mtab();
1763 SAFE_FREE(mountent.mnt_opts);
1764 if (rc)
1765 rc = EX_FILEIO;
1766 mount_exit:
1767 if(mountpassword) {
1768 int len = strlen(mountpassword);
1769 memset(mountpassword,0,len);
1770 SAFE_FREE(mountpassword);
1773 if (addrhead)
1774 freeaddrinfo(addrhead);
1775 SAFE_FREE(options);
1776 SAFE_FREE(orgoptions);
1777 SAFE_FREE(resolved_path);
1778 SAFE_FREE(share_name);
1779 exit(rc);