libwbclient: Zero out context on initialization
[Samba.git] / client / mount.cifs.c
blobd307f4f012037c0e02cdf77bac51e120253bc5fc
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 #ifndef SAFE_FREE
80 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
81 #endif
83 #define MOUNT_PASSWD_SIZE 128
84 #define DOMAIN_SIZE 64
86 /* currently maximum length of IPv6 address string */
87 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
90 * mount.cifs has been the subject of many "security" bugs that have arisen
91 * because of users and distributions installing it as a setuid root program.
92 * mount.cifs has not been audited for security. Thus, we strongly recommend
93 * that it not be installed setuid root. To make that abundantly clear,
94 * mount.cifs now check whether it's running setuid root and exit with an
95 * error if it is. If you wish to disable this check, then set the following
96 * #define to 1, but please realize that you do so at your own peril.
98 #define CIFS_DISABLE_SETUID_CHECK 0
101 * By default, mount.cifs follows the conventions set forth by /bin/mount
102 * for user mounts. That is, it requires that the mount be listed in
103 * /etc/fstab with the "user" option when run as an unprivileged user and
104 * mount.cifs is setuid root.
106 * Older versions of mount.cifs however were "looser" in this regard. When
107 * made setuid root, a user could run mount.cifs directly and mount any share
108 * on a directory owned by that user.
110 * The legacy behavior is now disabled by default. To reenable it, set the
111 * following #define to true.
113 #define CIFS_LEGACY_SETUID_CHECK 0
116 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
117 * flags by default. These defaults can be changed here.
119 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
121 const char *thisprogram;
122 int verboseflag = 0;
123 int fakemnt = 0;
124 static int got_password = 0;
125 static int got_user = 0;
126 static int got_domain = 0;
127 static int got_ip = 0;
128 static int got_unc = 0;
129 static int got_uid = 0;
130 static int got_gid = 0;
131 static char * user_name = NULL;
132 static char * mountpassword = NULL;
133 char * domain_name = NULL;
134 char * prefixpath = NULL;
135 const char *cifs_fstype = "cifs";
137 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
138 * don't link to libreplace so need them here. */
140 /* like strncpy but does not 0 fill the buffer and always null
141 * terminates. bufsize is the size of the destination buffer */
143 #ifndef HAVE_STRLCPY
144 static size_t strlcpy(char *d, const char *s, size_t bufsize)
146 size_t len = strlen(s);
147 size_t ret = len;
148 if (bufsize <= 0) return 0;
149 if (len >= bufsize) len = bufsize-1;
150 memcpy(d, s, len);
151 d[len] = 0;
152 return ret;
154 #endif
156 /* like strncat but does not 0 fill the buffer and always null
157 * terminates. bufsize is the length of the buffer, which should
158 * be one more than the maximum resulting string length */
160 #ifndef HAVE_STRLCAT
161 static size_t strlcat(char *d, const char *s, size_t bufsize)
163 size_t len1 = strlen(d);
164 size_t len2 = strlen(s);
165 size_t ret = len1 + len2;
167 if (len1+len2 >= bufsize) {
168 if (bufsize < (len1+1)) {
169 return ret;
171 len2 = bufsize - (len1+1);
173 if (len2 > 0) {
174 memcpy(d+len1, s, len2);
175 d[len1+len2] = 0;
177 return ret;
179 #endif
182 * If an unprivileged user is doing the mounting then we need to ensure
183 * that the entry is in /etc/fstab.
185 static int
186 check_mountpoint(const char *progname, char *mountpoint)
188 int err;
189 struct stat statbuf;
191 /* does mountpoint exist and is it a directory? */
192 err = stat(".", &statbuf);
193 if (err) {
194 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
195 mountpoint, strerror(errno));
196 return EX_USAGE;
199 if (!S_ISDIR(statbuf.st_mode)) {
200 fprintf(stderr, "%s: %s is not a directory!", progname,
201 mountpoint);
202 return EX_USAGE;
205 #if CIFS_LEGACY_SETUID_CHECK
206 /* do extra checks on mountpoint for legacy setuid behavior */
207 if (!getuid() || geteuid())
208 return 0;
210 if (statbuf.st_uid != getuid()) {
211 fprintf(stderr, "%s: %s is not owned by user\n", progname,
212 mountpoint);
213 return EX_USAGE;
216 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
217 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
218 mountpoint);
219 return EX_USAGE;
221 #endif /* CIFS_LEGACY_SETUID_CHECK */
223 return 0;
226 #if CIFS_DISABLE_SETUID_CHECK
227 static int
228 check_setuid(void)
230 return 0;
232 #else /* CIFS_DISABLE_SETUID_CHECK */
233 static int
234 check_setuid(void)
236 if (getuid() && !geteuid()) {
237 printf("This mount.cifs program has been built with the "
238 "ability to run as a setuid root program disabled.\n"
239 "mount.cifs has not been well audited for security "
240 "holes. Therefore the Samba team does not recommend "
241 "installing it as a setuid root program.\n");
242 return 1;
245 return 0;
247 #endif /* CIFS_DISABLE_SETUID_CHECK */
249 #if CIFS_LEGACY_SETUID_CHECK
250 static int
251 check_fstab(const char *progname, char *mountpoint, char *devname,
252 char **options)
254 return 0;
256 #else /* CIFS_LEGACY_SETUID_CHECK */
257 static int
258 check_fstab(const char *progname, char *mountpoint, char *devname,
259 char **options)
261 FILE *fstab;
262 struct mntent *mnt;
264 /* make sure this mount is listed in /etc/fstab */
265 fstab = setmntent(_PATH_FSTAB, "r");
266 if (!fstab) {
267 fprintf(stderr, "Couldn't open %s for reading!\n",
268 _PATH_FSTAB);
269 return EX_FILEIO;
272 while((mnt = getmntent(fstab))) {
273 if (!strcmp(mountpoint, mnt->mnt_dir))
274 break;
276 endmntent(fstab);
278 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
279 fprintf(stderr, "%s: permission denied: no match for "
280 "%s found in %s\n", progname, mountpoint,
281 _PATH_FSTAB);
282 return EX_USAGE;
286 * 'mount' munges the options from fstab before passing them
287 * to us. It is non-trivial to test that we have the correct
288 * set of options. We don't want to trust what the user
289 * gave us, so just take whatever is in /etc/fstab.
291 free(*options);
292 *options = strdup(mnt->mnt_opts);
293 return 0;
295 #endif /* CIFS_LEGACY_SETUID_CHECK */
297 /* BB finish BB
299 cifs_umount
300 open nofollow - avoid symlink exposure?
301 get owner of dir see if matches self or if root
302 call system(umount argv) etc.
304 BB end finish BB */
306 static char * check_for_domain(char **);
309 static void mount_cifs_usage(FILE *stream)
311 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
312 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
313 fprintf(stream, " to a local directory.\n\nOptions:\n");
314 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
315 fprintf(stream, "\nLess commonly used options:");
316 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
317 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
318 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
319 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
320 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
321 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
322 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
323 fprintf(stream, "\n\nRarely used options:");
324 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
325 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
326 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
327 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
328 fprintf(stream, "\n\tman 8 mount.cifs\n");
329 fprintf(stream, "\nTo display the version number of the mount helper:");
330 fprintf(stream, "\n\t%s -V\n",thisprogram);
332 SAFE_FREE(mountpassword);
334 if (stream == stderr)
335 exit(EX_USAGE);
336 exit(0);
339 /* caller frees username if necessary */
340 static char * getusername(void) {
341 char *username = NULL;
342 struct passwd *password = getpwuid(getuid());
344 if (password) {
345 username = password->pw_name;
347 return username;
350 static int open_cred_file(char * file_name)
352 char * line_buf;
353 char * temp_val;
354 FILE * fs;
355 int i, length;
357 i = access(file_name, R_OK);
358 if (i)
359 return i;
361 fs = fopen(file_name,"r");
362 if(fs == NULL)
363 return errno;
364 line_buf = (char *)malloc(4096);
365 if(line_buf == NULL) {
366 fclose(fs);
367 return ENOMEM;
370 while(fgets(line_buf,4096,fs)) {
371 /* parse line from credential file */
373 /* eat leading white space */
374 for(i=0;i<4086;i++) {
375 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
376 break;
377 /* if whitespace - skip past it */
379 if (strncasecmp("username",line_buf+i,8) == 0) {
380 temp_val = strchr(line_buf + i,'=');
381 if(temp_val) {
382 /* go past equals sign */
383 temp_val++;
384 for(length = 0;length<4087;length++) {
385 if ((temp_val[length] == '\n')
386 || (temp_val[length] == '\0')) {
387 temp_val[length] = '\0';
388 break;
391 if(length > 4086) {
392 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
393 memset(line_buf,0,4096);
394 exit(EX_USAGE);
395 } else {
396 got_user = 1;
397 user_name = (char *)calloc(1 + length,1);
398 /* BB adding free of user_name string before exit,
399 not really necessary but would be cleaner */
400 strlcpy(user_name,temp_val, length+1);
403 } else if (strncasecmp("password",line_buf+i,8) == 0) {
404 temp_val = strchr(line_buf+i,'=');
405 if(temp_val) {
406 /* go past equals sign */
407 temp_val++;
408 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
409 if ((temp_val[length] == '\n')
410 || (temp_val[length] == '\0')) {
411 temp_val[length] = '\0';
412 break;
415 if(length > MOUNT_PASSWD_SIZE) {
416 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
417 memset(line_buf,0, 4096);
418 exit(EX_USAGE);
419 } else {
420 if(mountpassword == NULL) {
421 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
422 } else
423 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
424 if(mountpassword) {
425 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
426 got_password = 1;
430 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
431 temp_val = strchr(line_buf+i,'=');
432 if(temp_val) {
433 /* go past equals sign */
434 temp_val++;
435 if(verboseflag)
436 fprintf(stderr, "\nDomain %s\n",temp_val);
437 for(length = 0;length<DOMAIN_SIZE+1;length++) {
438 if ((temp_val[length] == '\n')
439 || (temp_val[length] == '\0')) {
440 temp_val[length] = '\0';
441 break;
444 if(length > DOMAIN_SIZE) {
445 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
446 exit(EX_USAGE);
447 } else {
448 if(domain_name == NULL) {
449 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
450 } else
451 memset(domain_name,0,DOMAIN_SIZE);
452 if(domain_name) {
453 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
454 got_domain = 1;
461 fclose(fs);
462 SAFE_FREE(line_buf);
463 return 0;
466 static int get_password_from_file(int file_descript, char * filename)
468 int rc = 0;
469 int i;
470 char c;
472 if(mountpassword == NULL)
473 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
474 else
475 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
477 if (mountpassword == NULL) {
478 fprintf(stderr, "malloc failed\n");
479 exit(EX_SYSERR);
482 if(filename != NULL) {
483 rc = access(filename, R_OK);
484 if (rc) {
485 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
486 filename, strerror(errno));
487 exit(EX_SYSERR);
489 file_descript = open(filename, O_RDONLY);
490 if(file_descript < 0) {
491 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
492 strerror(errno),filename);
493 exit(EX_SYSERR);
496 /* else file already open and fd provided */
498 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
499 rc = read(file_descript,&c,1);
500 if(rc < 0) {
501 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
502 if(filename != NULL)
503 close(file_descript);
504 exit(EX_SYSERR);
505 } else if(rc == 0) {
506 if(mountpassword[0] == 0) {
507 if(verboseflag)
508 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
510 break;
511 } else /* read valid character */ {
512 if((c == 0) || (c == '\n')) {
513 mountpassword[i] = '\0';
514 break;
515 } else
516 mountpassword[i] = c;
519 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
520 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
521 MOUNT_PASSWD_SIZE);
523 got_password = 1;
524 if(filename != NULL) {
525 close(file_descript);
528 return rc;
531 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
533 const char * data;
534 char * percent_char = NULL;
535 char * value = NULL;
536 char * next_keyword = NULL;
537 char * out = NULL;
538 int out_len = 0;
539 int word_len;
540 int rc = 0;
541 char user[32];
542 char group[32];
544 if (!optionsp || !*optionsp)
545 return 1;
546 data = *optionsp;
548 /* BB fixme check for separator override BB */
550 if (getuid()) {
551 got_uid = 1;
552 snprintf(user,sizeof(user),"%u",getuid());
553 got_gid = 1;
554 snprintf(group,sizeof(group),"%u",getgid());
557 /* while ((data = strsep(&options, ",")) != NULL) { */
558 while(data != NULL) {
559 /* check if ends with trailing comma */
560 if(*data == 0)
561 break;
563 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
564 /* data = next keyword */
565 /* value = next value ie stuff after equal sign */
567 next_keyword = strchr(data,','); /* BB handle sep= */
569 /* temporarily null terminate end of keyword=value pair */
570 if(next_keyword)
571 *next_keyword++ = 0;
573 /* temporarily null terminate keyword to make keyword and value distinct */
574 if ((value = strchr(data, '=')) != NULL) {
575 *value = '\0';
576 value++;
579 if (strncmp(data, "users",5) == 0) {
580 if(!value || !*value) {
581 *filesys_flags |= MS_USERS;
582 goto nocopy;
584 } else if (strncmp(data, "user_xattr",10) == 0) {
585 /* do nothing - need to skip so not parsed as user name */
586 } else if (strncmp(data, "user", 4) == 0) {
588 if (!value || !*value) {
589 if(data[4] == '\0') {
590 *filesys_flags |= MS_USER;
591 goto nocopy;
592 } else {
593 fprintf(stderr, "username specified with no parameter\n");
594 SAFE_FREE(out);
595 return 1; /* needs_arg; */
597 } else {
598 if (strnlen(value, 260) < 260) {
599 got_user=1;
600 percent_char = strchr(value,'%');
601 if(percent_char) {
602 *percent_char = ',';
603 if(mountpassword == NULL)
604 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
605 if(mountpassword) {
606 if(got_password)
607 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
608 got_password = 1;
609 percent_char++;
610 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
611 /* remove password from username */
612 while(*percent_char != 0) {
613 *percent_char = ',';
614 percent_char++;
618 /* this is only case in which the user
619 name buf is not malloc - so we have to
620 check for domain name embedded within
621 the user name here since the later
622 call to check_for_domain will not be
623 invoked */
624 domain_name = check_for_domain(&value);
625 } else {
626 fprintf(stderr, "username too long\n");
627 SAFE_FREE(out);
628 return 1;
631 } else if (strncmp(data, "pass", 4) == 0) {
632 if (!value || !*value) {
633 if(got_password) {
634 fprintf(stderr, "\npassword specified twice, ignoring second\n");
635 } else
636 got_password = 1;
637 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
638 if (got_password) {
639 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
640 } else {
641 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
642 if (!mountpassword) {
643 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
644 SAFE_FREE(out);
645 return 1;
647 got_password = 1;
649 } else {
650 fprintf(stderr, "password too long\n");
651 SAFE_FREE(out);
652 return 1;
654 goto nocopy;
655 } else if (strncmp(data, "sec", 3) == 0) {
656 if (value) {
657 if (!strncmp(value, "none", 4) ||
658 !strncmp(value, "krb5", 4))
659 got_password = 1;
661 } else if (strncmp(data, "ip", 2) == 0) {
662 if (!value || !*value) {
663 fprintf(stderr, "target ip address argument missing");
664 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
665 if(verboseflag)
666 fprintf(stderr, "ip address %s override specified\n",value);
667 got_ip = 1;
668 } else {
669 fprintf(stderr, "ip address too long\n");
670 SAFE_FREE(out);
671 return 1;
673 } else if ((strncmp(data, "unc", 3) == 0)
674 || (strncmp(data, "target", 6) == 0)
675 || (strncmp(data, "path", 4) == 0)) {
676 if (!value || !*value) {
677 fprintf(stderr, "invalid path to network resource\n");
678 SAFE_FREE(out);
679 return 1; /* needs_arg; */
680 } else if(strnlen(value,5) < 5) {
681 fprintf(stderr, "UNC name too short");
684 if (strnlen(value, 300) < 300) {
685 got_unc = 1;
686 if (strncmp(value, "//", 2) == 0) {
687 if(got_unc)
688 fprintf(stderr, "unc name specified twice, ignoring second\n");
689 else
690 got_unc = 1;
691 } else if (strncmp(value, "\\\\", 2) != 0) {
692 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
693 SAFE_FREE(out);
694 return 1;
695 } else {
696 if(got_unc)
697 fprintf(stderr, "unc name specified twice, ignoring second\n");
698 else
699 got_unc = 1;
701 } else {
702 fprintf(stderr, "CIFS: UNC name too long\n");
703 SAFE_FREE(out);
704 return 1;
706 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
707 || (strncmp(data, "workg", 5) == 0)) {
708 /* note this allows for synonyms of "domain"
709 such as "DOM" and "dom" and "workgroup"
710 and "WORKGRP" etc. */
711 if (!value || !*value) {
712 fprintf(stderr, "CIFS: invalid domain name\n");
713 SAFE_FREE(out);
714 return 1; /* needs_arg; */
716 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
717 got_domain = 1;
718 } else {
719 fprintf(stderr, "domain name too long\n");
720 SAFE_FREE(out);
721 return 1;
723 } else if (strncmp(data, "cred", 4) == 0) {
724 if (value && *value) {
725 rc = open_cred_file(value);
726 if(rc) {
727 fprintf(stderr, "error %d (%s) opening credential file %s\n",
728 rc, strerror(rc), value);
729 SAFE_FREE(out);
730 return 1;
732 } else {
733 fprintf(stderr, "invalid credential file name specified\n");
734 SAFE_FREE(out);
735 return 1;
737 } else if (strncmp(data, "uid", 3) == 0) {
738 if (value && *value) {
739 got_uid = 1;
740 if (!isdigit(*value)) {
741 struct passwd *pw;
743 if (!(pw = getpwnam(value))) {
744 fprintf(stderr, "bad user name \"%s\"\n", value);
745 exit(EX_USAGE);
747 snprintf(user, sizeof(user), "%u", pw->pw_uid);
748 } else {
749 strlcpy(user,value,sizeof(user));
752 goto nocopy;
753 } else if (strncmp(data, "gid", 3) == 0) {
754 if (value && *value) {
755 got_gid = 1;
756 if (!isdigit(*value)) {
757 struct group *gr;
759 if (!(gr = getgrnam(value))) {
760 fprintf(stderr, "bad group name \"%s\"\n", value);
761 exit(EX_USAGE);
763 snprintf(group, sizeof(group), "%u", gr->gr_gid);
764 } else {
765 strlcpy(group,value,sizeof(group));
768 goto nocopy;
769 /* fmask and dmask synonyms for people used to smbfs syntax */
770 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
771 if (!value || !*value) {
772 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
773 SAFE_FREE(out);
774 return 1;
777 if (value[0] != '0') {
778 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
781 if (strcmp (data, "fmask") == 0) {
782 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
783 data = "file_mode"; /* BB fix this */
785 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
786 if (!value || !*value) {
787 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
788 SAFE_FREE(out);
789 return 1;
792 if (value[0] != '0') {
793 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
796 if (strcmp (data, "dmask") == 0) {
797 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
798 data = "dir_mode";
800 /* the following eight mount options should be
801 stripped out from what is passed into the kernel
802 since these eight options are best passed as the
803 mount flags rather than redundantly to the kernel
804 and could generate spurious warnings depending on the
805 level of the corresponding cifs vfs kernel code */
806 } else if (strncmp(data, "nosuid", 6) == 0) {
807 *filesys_flags |= MS_NOSUID;
808 } else if (strncmp(data, "suid", 4) == 0) {
809 *filesys_flags &= ~MS_NOSUID;
810 } else if (strncmp(data, "nodev", 5) == 0) {
811 *filesys_flags |= MS_NODEV;
812 } else if ((strncmp(data, "nobrl", 5) == 0) ||
813 (strncmp(data, "nolock", 6) == 0)) {
814 *filesys_flags &= ~MS_MANDLOCK;
815 } else if (strncmp(data, "dev", 3) == 0) {
816 *filesys_flags &= ~MS_NODEV;
817 } else if (strncmp(data, "noexec", 6) == 0) {
818 *filesys_flags |= MS_NOEXEC;
819 } else if (strncmp(data, "exec", 4) == 0) {
820 *filesys_flags &= ~MS_NOEXEC;
821 } else if (strncmp(data, "guest", 5) == 0) {
822 user_name = (char *)calloc(1, 1);
823 got_user = 1;
824 got_password = 1;
825 } else if (strncmp(data, "ro", 2) == 0) {
826 *filesys_flags |= MS_RDONLY;
827 goto nocopy;
828 } else if (strncmp(data, "rw", 2) == 0) {
829 *filesys_flags &= ~MS_RDONLY;
830 goto nocopy;
831 } else if (strncmp(data, "remount", 7) == 0) {
832 *filesys_flags |= MS_REMOUNT;
833 } /* else if (strnicmp(data, "port", 4) == 0) {
834 if (value && *value) {
835 vol->port =
836 simple_strtoul(value, &value, 0);
838 } else if (strnicmp(data, "rsize", 5) == 0) {
839 if (value && *value) {
840 vol->rsize =
841 simple_strtoul(value, &value, 0);
843 } else if (strnicmp(data, "wsize", 5) == 0) {
844 if (value && *value) {
845 vol->wsize =
846 simple_strtoul(value, &value, 0);
848 } else if (strnicmp(data, "version", 3) == 0) {
849 } else {
850 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
851 } */ /* nothing to do on those four mount options above.
852 Just pass to kernel and ignore them here */
854 /* Copy (possibly modified) option to out */
855 word_len = strlen(data);
856 if (value)
857 word_len += 1 + strlen(value);
859 out = (char *)realloc(out, out_len + word_len + 2);
860 if (out == NULL) {
861 perror("malloc");
862 exit(EX_SYSERR);
865 if (out_len) {
866 strlcat(out, ",", out_len + word_len + 2);
867 out_len++;
870 if (value)
871 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
872 else
873 snprintf(out + out_len, word_len + 1, "%s", data);
874 out_len = strlen(out);
876 nocopy:
877 data = next_keyword;
880 /* special-case the uid and gid */
881 if (got_uid) {
882 word_len = strlen(user);
884 out = (char *)realloc(out, out_len + word_len + 6);
885 if (out == NULL) {
886 perror("malloc");
887 exit(EX_SYSERR);
890 if (out_len) {
891 strlcat(out, ",", out_len + word_len + 6);
892 out_len++;
894 snprintf(out + out_len, word_len + 5, "uid=%s", user);
895 out_len = strlen(out);
897 if (got_gid) {
898 word_len = strlen(group);
900 out = (char *)realloc(out, out_len + 1 + word_len + 6);
901 if (out == NULL) {
902 perror("malloc");
903 exit(EX_SYSERR);
906 if (out_len) {
907 strlcat(out, ",", out_len + word_len + 6);
908 out_len++;
910 snprintf(out + out_len, word_len + 5, "gid=%s", group);
911 out_len = strlen(out);
914 SAFE_FREE(*optionsp);
915 *optionsp = out;
916 return 0;
919 /* replace all (one or more) commas with double commas */
920 static void check_for_comma(char ** ppasswrd)
922 char *new_pass_buf;
923 char *pass;
924 int i,j;
925 int number_of_commas = 0;
926 int len;
928 if(ppasswrd == NULL)
929 return;
930 else
931 (pass = *ppasswrd);
933 len = strlen(pass);
935 for(i=0;i<len;i++) {
936 if(pass[i] == ',')
937 number_of_commas++;
940 if(number_of_commas == 0)
941 return;
942 if(number_of_commas > MOUNT_PASSWD_SIZE) {
943 /* would otherwise overflow the mount options buffer */
944 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
945 return;
948 new_pass_buf = (char *)malloc(len+number_of_commas+1);
949 if(new_pass_buf == NULL)
950 return;
952 for(i=0,j=0;i<len;i++,j++) {
953 new_pass_buf[j] = pass[i];
954 if(pass[i] == ',') {
955 j++;
956 new_pass_buf[j] = pass[i];
959 new_pass_buf[len+number_of_commas] = 0;
961 SAFE_FREE(*ppasswrd);
962 *ppasswrd = new_pass_buf;
964 return;
967 /* Usernames can not have backslash in them and we use
968 [BB check if usernames can have forward slash in them BB]
969 backslash as domain\user separator character
971 static char * check_for_domain(char **ppuser)
973 char * original_string;
974 char * usernm;
975 char * domainnm;
976 int original_len;
977 int len;
978 int i;
980 if(ppuser == NULL)
981 return NULL;
983 original_string = *ppuser;
985 if (original_string == NULL)
986 return NULL;
988 original_len = strlen(original_string);
990 usernm = strchr(*ppuser,'/');
991 if (usernm == NULL) {
992 usernm = strchr(*ppuser,'\\');
993 if (usernm == NULL)
994 return NULL;
997 if(got_domain) {
998 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
999 return NULL;
1002 usernm[0] = 0;
1003 domainnm = *ppuser;
1004 if (domainnm[0] != 0) {
1005 got_domain = 1;
1006 } else {
1007 fprintf(stderr, "null domain\n");
1009 len = strlen(domainnm);
1010 /* reset domainm to new buffer, and copy
1011 domain name into it */
1012 domainnm = (char *)malloc(len+1);
1013 if(domainnm == NULL)
1014 return NULL;
1016 strlcpy(domainnm,*ppuser,len+1);
1018 /* move_string(*ppuser, usernm+1) */
1019 len = strlen(usernm+1);
1021 if(len >= original_len) {
1022 /* should not happen */
1023 return domainnm;
1026 for(i=0;i<original_len;i++) {
1027 if(i<len)
1028 original_string[i] = usernm[i+1];
1029 else /* stuff with commas to remove last parm */
1030 original_string[i] = ',';
1033 /* BB add check for more than one slash?
1034 strchr(*ppuser,'/');
1035 strchr(*ppuser,'\\')
1038 return domainnm;
1041 /* replace all occurances of "from" in a string with "to" */
1042 static void replace_char(char *string, char from, char to, int maxlen)
1044 char *lastchar = string + maxlen;
1045 while (string) {
1046 string = strchr(string, from);
1047 if (string) {
1048 *string = to;
1049 if (string >= lastchar)
1050 return;
1055 /* Note that caller frees the returned buffer if necessary */
1056 static struct addrinfo *
1057 parse_server(char ** punc_name)
1059 char * unc_name = *punc_name;
1060 int length = strnlen(unc_name, MAX_UNC_LEN);
1061 char * share;
1062 struct addrinfo *addrlist;
1063 int rc;
1065 if(length > (MAX_UNC_LEN - 1)) {
1066 fprintf(stderr, "mount error: UNC name too long");
1067 return NULL;
1069 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1070 (strncasecmp("smb://", unc_name, 6) == 0)) {
1071 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1072 return NULL;
1075 if(length < 3) {
1076 /* BB add code to find DFS root here */
1077 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1078 return NULL;
1079 } else {
1080 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1081 /* check for nfs syntax ie server:share */
1082 share = strchr(unc_name,':');
1083 if(share) {
1084 *punc_name = (char *)malloc(length+3);
1085 if(*punc_name == NULL) {
1086 /* put the original string back if
1087 no memory left */
1088 *punc_name = unc_name;
1089 return NULL;
1091 *share = '/';
1092 strlcpy((*punc_name)+2,unc_name,length+1);
1093 SAFE_FREE(unc_name);
1094 unc_name = *punc_name;
1095 unc_name[length+2] = 0;
1096 goto continue_unc_parsing;
1097 } else {
1098 fprintf(stderr, "mount error: improperly formatted UNC name.");
1099 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1100 return NULL;
1102 } else {
1103 continue_unc_parsing:
1104 unc_name[0] = '/';
1105 unc_name[1] = '/';
1106 unc_name += 2;
1108 /* allow for either delimiter between host and sharename */
1109 if ((share = strpbrk(unc_name, "/\\"))) {
1110 *share = 0; /* temporarily terminate the string */
1111 share += 1;
1112 if(got_ip == 0) {
1113 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1114 if (rc != 0) {
1115 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1116 unc_name, gai_strerror(rc));
1117 addrlist = NULL;
1120 *(share - 1) = '/'; /* put delimiter back */
1122 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1123 if ((prefixpath = strpbrk(share, "/\\"))) {
1124 *prefixpath = 0; /* permanently terminate the string */
1125 if (!strlen(++prefixpath))
1126 prefixpath = NULL; /* this needs to be done explicitly */
1128 if(got_ip) {
1129 if(verboseflag)
1130 fprintf(stderr, "ip address specified explicitly\n");
1131 return NULL;
1133 /* BB should we pass an alternate version of the share name as Unicode */
1135 return addrlist;
1136 } else {
1137 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1138 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1139 return NULL;
1145 static struct option longopts[] = {
1146 { "all", 0, NULL, 'a' },
1147 { "help",0, NULL, 'h' },
1148 { "move",0, NULL, 'm' },
1149 { "bind",0, NULL, 'b' },
1150 { "read-only", 0, NULL, 'r' },
1151 { "ro", 0, NULL, 'r' },
1152 { "verbose", 0, NULL, 'v' },
1153 { "version", 0, NULL, 'V' },
1154 { "read-write", 0, NULL, 'w' },
1155 { "rw", 0, NULL, 'w' },
1156 { "options", 1, NULL, 'o' },
1157 { "type", 1, NULL, 't' },
1158 { "rsize",1, NULL, 'R' },
1159 { "wsize",1, NULL, 'W' },
1160 { "uid", 1, NULL, '1'},
1161 { "gid", 1, NULL, '2'},
1162 { "user",1,NULL,'u'},
1163 { "username",1,NULL,'u'},
1164 { "dom",1,NULL,'d'},
1165 { "domain",1,NULL,'d'},
1166 { "password",1,NULL,'p'},
1167 { "pass",1,NULL,'p'},
1168 { "credentials",1,NULL,'c'},
1169 { "port",1,NULL,'P'},
1170 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1171 { NULL, 0, NULL, 0 }
1174 /* convert a string to uppercase. return false if the string
1175 * wasn't ASCII. Return success on a NULL ptr */
1176 static int
1177 uppercase_string(char *string)
1179 if (!string)
1180 return 1;
1182 while (*string) {
1183 /* check for unicode */
1184 if ((unsigned char) string[0] & 0x80)
1185 return 0;
1186 *string = toupper((unsigned char) *string);
1187 string++;
1190 return 1;
1193 static void print_cifs_mount_version(void)
1195 printf("mount.cifs version: %s.%s%s\n",
1196 MOUNT_CIFS_VERSION_MAJOR,
1197 MOUNT_CIFS_VERSION_MINOR,
1198 MOUNT_CIFS_VENDOR_SUFFIX);
1202 * This function borrowed from fuse-utils...
1204 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1205 * newlines embedded within the text fields. To make sure no one corrupts
1206 * the mtab, fail the mount if there are embedded newlines.
1208 static int check_newline(const char *progname, const char *name)
1210 const char *s;
1211 for (s = "\n"; *s; s++) {
1212 if (strchr(name, *s)) {
1213 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1214 progname, *s);
1215 return EX_USAGE;
1218 return 0;
1221 static int check_mtab(const char *progname, const char *devname,
1222 const char *dir)
1224 if (check_newline(progname, devname) == -1 ||
1225 check_newline(progname, dir) == -1)
1226 return EX_USAGE;
1227 return 0;
1231 int main(int argc, char ** argv)
1233 int c;
1234 unsigned long flags = MS_MANDLOCK;
1235 char * orgoptions = NULL;
1236 char * share_name = NULL;
1237 const char * ipaddr = NULL;
1238 char * uuid = NULL;
1239 char * mountpoint = NULL;
1240 char * options = NULL;
1241 char * optionstail;
1242 char * resolved_path = NULL;
1243 char * temp;
1244 char * dev_name;
1245 int rc = 0;
1246 int rsize = 0;
1247 int wsize = 0;
1248 int nomtab = 0;
1249 int uid = 0;
1250 int gid = 0;
1251 int optlen = 0;
1252 int orgoptlen = 0;
1253 size_t options_size = 0;
1254 size_t current_len;
1255 int retry = 0; /* set when we have to retry mount with uppercase */
1256 struct addrinfo *addrhead = NULL, *addr;
1257 struct utsname sysinfo;
1258 struct mntent mountent;
1259 struct sockaddr_in *addr4;
1260 struct sockaddr_in6 *addr6;
1261 FILE * pmntfile;
1263 if (check_setuid())
1264 return EX_USAGE;
1266 /* setlocale(LC_ALL, "");
1267 bindtextdomain(PACKAGE, LOCALEDIR);
1268 textdomain(PACKAGE); */
1270 if(argc && argv)
1271 thisprogram = argv[0];
1272 else
1273 mount_cifs_usage(stderr);
1275 if(thisprogram == NULL)
1276 thisprogram = "mount.cifs";
1278 uname(&sysinfo);
1279 /* BB add workstation name and domain and pass down */
1281 /* #ifdef _GNU_SOURCE
1282 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1283 #endif */
1284 if(argc > 2) {
1285 dev_name = argv[1];
1286 share_name = strndup(argv[1], MAX_UNC_LEN);
1287 if (share_name == NULL) {
1288 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1289 exit(EX_SYSERR);
1291 mountpoint = argv[2];
1292 } else if (argc == 2) {
1293 if ((strcmp(argv[1], "-V") == 0) ||
1294 (strcmp(argv[1], "--version") == 0))
1296 print_cifs_mount_version();
1297 exit(0);
1300 if ((strcmp(argv[1], "-h") == 0) ||
1301 (strcmp(argv[1], "-?") == 0) ||
1302 (strcmp(argv[1], "--help") == 0))
1303 mount_cifs_usage(stdout);
1305 mount_cifs_usage(stderr);
1306 } else {
1307 mount_cifs_usage(stderr);
1311 /* add sharename in opts string as unc= parm */
1312 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1313 longopts, NULL)) != -1) {
1314 switch (c) {
1315 /* No code to do the following options yet */
1316 /* case 'l':
1317 list_with_volumelabel = 1;
1318 break;
1319 case 'L':
1320 volumelabel = optarg;
1321 break; */
1322 /* case 'a':
1323 ++mount_all;
1324 break; */
1326 case '?':
1327 case 'h': /* help */
1328 mount_cifs_usage(stdout);
1329 case 'n':
1330 ++nomtab;
1331 break;
1332 case 'b':
1333 #ifdef MS_BIND
1334 flags |= MS_BIND;
1335 #else
1336 fprintf(stderr,
1337 "option 'b' (MS_BIND) not supported\n");
1338 #endif
1339 break;
1340 case 'm':
1341 #ifdef MS_MOVE
1342 flags |= MS_MOVE;
1343 #else
1344 fprintf(stderr,
1345 "option 'm' (MS_MOVE) not supported\n");
1346 #endif
1347 break;
1348 case 'o':
1349 orgoptions = strdup(optarg);
1350 break;
1351 case 'r': /* mount readonly */
1352 flags |= MS_RDONLY;
1353 break;
1354 case 'U':
1355 uuid = optarg;
1356 break;
1357 case 'v':
1358 ++verboseflag;
1359 break;
1360 case 'V':
1361 print_cifs_mount_version();
1362 exit (0);
1363 case 'w':
1364 flags &= ~MS_RDONLY;
1365 break;
1366 case 'R':
1367 rsize = atoi(optarg) ;
1368 break;
1369 case 'W':
1370 wsize = atoi(optarg);
1371 break;
1372 case '1':
1373 if (isdigit(*optarg)) {
1374 char *ep;
1376 uid = strtoul(optarg, &ep, 10);
1377 if (*ep) {
1378 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1379 exit(EX_USAGE);
1381 } else {
1382 struct passwd *pw;
1384 if (!(pw = getpwnam(optarg))) {
1385 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1386 exit(EX_USAGE);
1388 uid = pw->pw_uid;
1389 endpwent();
1391 break;
1392 case '2':
1393 if (isdigit(*optarg)) {
1394 char *ep;
1396 gid = strtoul(optarg, &ep, 10);
1397 if (*ep) {
1398 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1399 exit(EX_USAGE);
1401 } else {
1402 struct group *gr;
1404 if (!(gr = getgrnam(optarg))) {
1405 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1406 exit(EX_USAGE);
1408 gid = gr->gr_gid;
1409 endpwent();
1411 break;
1412 case 'u':
1413 got_user = 1;
1414 user_name = optarg;
1415 break;
1416 case 'd':
1417 domain_name = optarg; /* BB fix this - currently ignored */
1418 got_domain = 1;
1419 break;
1420 case 'p':
1421 if(mountpassword == NULL)
1422 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1423 if(mountpassword) {
1424 got_password = 1;
1425 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1427 break;
1428 case 'S':
1429 get_password_from_file(0 /* stdin */,NULL);
1430 break;
1431 case 't':
1432 break;
1433 case 'f':
1434 ++fakemnt;
1435 break;
1436 default:
1437 fprintf(stderr, "unknown mount option %c\n",c);
1438 mount_cifs_usage(stderr);
1442 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1443 mount_cifs_usage(stderr);
1446 /* make sure mountpoint is legit */
1447 rc = chdir(mountpoint);
1448 if (rc) {
1449 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1450 strerror(errno));
1451 rc = EX_USAGE;
1452 goto mount_exit;
1455 rc = check_mountpoint(thisprogram, mountpoint);
1456 if (rc)
1457 goto mount_exit;
1459 /* sanity check for unprivileged mounts */
1460 if (getuid()) {
1461 rc = check_fstab(thisprogram, mountpoint, dev_name,
1462 &orgoptions);
1463 if (rc)
1464 goto mount_exit;
1466 /* enable any default user mount flags */
1467 flags |= CIFS_SETUID_FLAGS;
1470 if (getenv("PASSWD")) {
1471 if(mountpassword == NULL)
1472 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1473 if(mountpassword) {
1474 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1475 got_password = 1;
1477 } else if (getenv("PASSWD_FD")) {
1478 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1479 } else if (getenv("PASSWD_FILE")) {
1480 get_password_from_file(0, getenv("PASSWD_FILE"));
1483 if (orgoptions && parse_options(&orgoptions, &flags)) {
1484 rc = EX_USAGE;
1485 goto mount_exit;
1488 if (getuid()) {
1489 #if !CIFS_LEGACY_SETUID_CHECK
1490 if (!(flags & (MS_USERS|MS_USER))) {
1491 fprintf(stderr, "%s: permission denied\n", thisprogram);
1492 rc = EX_USAGE;
1493 goto mount_exit;
1495 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1497 if (geteuid()) {
1498 fprintf(stderr, "%s: not installed setuid - \"user\" "
1499 "CIFS mounts not supported.",
1500 thisprogram);
1501 rc = EX_FAIL;
1502 goto mount_exit;
1506 flags &= ~(MS_USERS|MS_USER);
1508 addrhead = addr = parse_server(&share_name);
1509 if((addrhead == NULL) && (got_ip == 0)) {
1510 fprintf(stderr, "No ip address specified and hostname not found\n");
1511 rc = EX_USAGE;
1512 goto mount_exit;
1515 /* BB save off path and pop after mount returns? */
1516 resolved_path = (char *)malloc(PATH_MAX+1);
1517 if (!resolved_path) {
1518 fprintf(stderr, "Unable to allocate memory.\n");
1519 rc = EX_SYSERR;
1520 goto mount_exit;
1523 /* Note that if we can not canonicalize the name, we get
1524 another chance to see if it is valid when we chdir to it */
1525 if(!realpath(".", resolved_path)) {
1526 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1527 mountpoint, strerror(errno));
1528 rc = EX_SYSERR;
1529 goto mount_exit;
1532 mountpoint = resolved_path;
1534 if(got_user == 0) {
1535 /* Note that the password will not be retrieved from the
1536 USER env variable (ie user%password form) as there is
1537 already a PASSWD environment varaible */
1538 if (getenv("USER"))
1539 user_name = strdup(getenv("USER"));
1540 if (user_name == NULL)
1541 user_name = getusername();
1542 got_user = 1;
1545 if(got_password == 0) {
1546 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1547 no good replacement yet. */
1548 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1549 if (!tmp_pass || !mountpassword) {
1550 fprintf(stderr, "Password not entered, exiting\n");
1551 exit(EX_USAGE);
1553 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1554 got_password = 1;
1556 /* FIXME launch daemon (handles dfs name resolution and credential change)
1557 remember to clear parms and overwrite password field before launching */
1558 if(orgoptions) {
1559 optlen = strlen(orgoptions);
1560 orgoptlen = optlen;
1561 } else
1562 optlen = 0;
1563 if(share_name)
1564 optlen += strlen(share_name) + 4;
1565 else {
1566 fprintf(stderr, "No server share name specified\n");
1567 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1568 exit(EX_USAGE);
1570 if(user_name)
1571 optlen += strlen(user_name) + 6;
1572 optlen += MAX_ADDRESS_LEN + 4;
1573 if(mountpassword)
1574 optlen += strlen(mountpassword) + 6;
1575 mount_retry:
1576 SAFE_FREE(options);
1577 options_size = optlen + 10 + DOMAIN_SIZE;
1578 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 */);
1580 if(options == NULL) {
1581 fprintf(stderr, "Could not allocate memory for mount options\n");
1582 exit(EX_SYSERR);
1585 strlcpy(options, "unc=", options_size);
1586 strlcat(options,share_name,options_size);
1587 /* scan backwards and reverse direction of slash */
1588 temp = strrchr(options, '/');
1589 if(temp > options + 6)
1590 *temp = '\\';
1591 if(user_name) {
1592 /* check for syntax like user=domain\user */
1593 if(got_domain == 0)
1594 domain_name = check_for_domain(&user_name);
1595 strlcat(options,",user=",options_size);
1596 strlcat(options,user_name,options_size);
1598 if(retry == 0) {
1599 if(domain_name) {
1600 /* extra length accounted for in option string above */
1601 strlcat(options,",domain=",options_size);
1602 strlcat(options,domain_name,options_size);
1606 strlcat(options,",ver=",options_size);
1607 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1609 if(orgoptions) {
1610 strlcat(options,",",options_size);
1611 strlcat(options,orgoptions,options_size);
1613 if(prefixpath) {
1614 strlcat(options,",prefixpath=",options_size);
1615 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1618 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1619 replace_char(dev_name, '\\', '/', strlen(share_name));
1621 if (!got_ip && addr) {
1622 strlcat(options, ",ip=", options_size);
1623 current_len = strnlen(options, options_size);
1624 optionstail = options + current_len;
1625 switch (addr->ai_addr->sa_family) {
1626 case AF_INET6:
1627 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1628 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1629 options_size - current_len);
1630 break;
1631 case AF_INET:
1632 addr4 = (struct sockaddr_in *) addr->ai_addr;
1633 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1634 options_size - current_len);
1635 break;
1636 default:
1637 ipaddr = NULL;
1640 /* if the address looks bogus, try the next one */
1641 if (!ipaddr) {
1642 addr = addr->ai_next;
1643 if (addr)
1644 goto mount_retry;
1645 rc = EX_SYSERR;
1646 goto mount_exit;
1650 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1651 strlcat(options, "%", options_size);
1652 current_len = strnlen(options, options_size);
1653 optionstail = options + current_len;
1654 snprintf(optionstail, options_size - current_len, "%u",
1655 addr6->sin6_scope_id);
1658 if(verboseflag)
1659 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1661 if (mountpassword) {
1663 * Commas have to be doubled, or else they will
1664 * look like the parameter separator
1666 if(retry == 0)
1667 check_for_comma(&mountpassword);
1668 strlcat(options,",pass=",options_size);
1669 strlcat(options,mountpassword,options_size);
1670 if (verboseflag)
1671 fprintf(stderr, ",pass=********");
1674 if (verboseflag)
1675 fprintf(stderr, "\n");
1677 rc = check_mtab(thisprogram, dev_name, mountpoint);
1678 if (rc)
1679 goto mount_exit;
1681 if (!fakemnt && mount(dev_name, ".", cifs_fstype, flags, options)) {
1682 switch (errno) {
1683 case ECONNREFUSED:
1684 case EHOSTUNREACH:
1685 if (addr) {
1686 addr = addr->ai_next;
1687 if (addr)
1688 goto mount_retry;
1690 break;
1691 case ENODEV:
1692 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1693 break;
1694 case ENXIO:
1695 if(retry == 0) {
1696 retry = 1;
1697 if (uppercase_string(dev_name) &&
1698 uppercase_string(share_name) &&
1699 uppercase_string(prefixpath)) {
1700 fprintf(stderr, "retrying with upper case share name\n");
1701 goto mount_retry;
1705 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1706 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1707 "mount.cifs)\n");
1708 rc = EX_FAIL;
1709 goto mount_exit;
1712 if (nomtab)
1713 goto mount_exit;
1714 atexit(unlock_mtab);
1715 rc = lock_mtab();
1716 if (rc) {
1717 fprintf(stderr, "cannot lock mtab");
1718 goto mount_exit;
1720 pmntfile = setmntent(MOUNTED, "a+");
1721 if (!pmntfile) {
1722 fprintf(stderr, "could not update mount table\n");
1723 unlock_mtab();
1724 rc = EX_FILEIO;
1725 goto mount_exit;
1727 mountent.mnt_fsname = dev_name;
1728 mountent.mnt_dir = mountpoint;
1729 mountent.mnt_type = (char *)(void *)cifs_fstype;
1730 mountent.mnt_opts = (char *)malloc(220);
1731 if(mountent.mnt_opts) {
1732 char * mount_user = getusername();
1733 memset(mountent.mnt_opts,0,200);
1734 if(flags & MS_RDONLY)
1735 strlcat(mountent.mnt_opts,"ro",220);
1736 else
1737 strlcat(mountent.mnt_opts,"rw",220);
1738 if(flags & MS_MANDLOCK)
1739 strlcat(mountent.mnt_opts,",mand",220);
1740 if(flags & MS_NOEXEC)
1741 strlcat(mountent.mnt_opts,",noexec",220);
1742 if(flags & MS_NOSUID)
1743 strlcat(mountent.mnt_opts,",nosuid",220);
1744 if(flags & MS_NODEV)
1745 strlcat(mountent.mnt_opts,",nodev",220);
1746 if(flags & MS_SYNCHRONOUS)
1747 strlcat(mountent.mnt_opts,",sync",220);
1748 if(mount_user) {
1749 if(getuid() != 0) {
1750 strlcat(mountent.mnt_opts,
1751 ",user=", 220);
1752 strlcat(mountent.mnt_opts,
1753 mount_user, 220);
1757 mountent.mnt_freq = 0;
1758 mountent.mnt_passno = 0;
1759 rc = addmntent(pmntfile,&mountent);
1760 endmntent(pmntfile);
1761 unlock_mtab();
1762 SAFE_FREE(mountent.mnt_opts);
1763 if (rc)
1764 rc = EX_FILEIO;
1765 mount_exit:
1766 if(mountpassword) {
1767 int len = strlen(mountpassword);
1768 memset(mountpassword,0,len);
1769 SAFE_FREE(mountpassword);
1772 if (addrhead)
1773 freeaddrinfo(addrhead);
1774 SAFE_FREE(options);
1775 SAFE_FREE(orgoptions);
1776 SAFE_FREE(resolved_path);
1777 SAFE_FREE(share_name);
1778 exit(rc);