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/>. */
29 #include <sys/types.h>
30 #include <sys/mount.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "14"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
62 #include "include/config.h"
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
80 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
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
;
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 */
144 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
146 size_t len
= strlen(s
);
148 if (bufsize
<= 0) return 0;
149 if (len
>= bufsize
) len
= bufsize
-1;
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 */
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)) {
171 len2
= bufsize
- (len1
+1);
174 memcpy(d
+len1
, s
, len2
);
182 * If an unprivileged user is doing the mounting then we need to ensure
183 * that the entry is in /etc/fstab.
186 check_mountpoint(const char *progname
, char *mountpoint
)
191 /* does mountpoint exist and is it a directory? */
192 err
= stat(".", &statbuf
);
194 fprintf(stderr
, "%s: failed to stat %s: %s\n", progname
,
195 mountpoint
, strerror(errno
));
199 if (!S_ISDIR(statbuf
.st_mode
)) {
200 fprintf(stderr
, "%s: %s is not a directory!", progname
,
205 #if CIFS_LEGACY_SETUID_CHECK
206 /* do extra checks on mountpoint for legacy setuid behavior */
207 if (!getuid() || geteuid())
210 if (statbuf
.st_uid
!= getuid()) {
211 fprintf(stderr
, "%s: %s is not owned by user\n", progname
,
216 if ((statbuf
.st_mode
& S_IRWXU
) != S_IRWXU
) {
217 fprintf(stderr
, "%s: invalid permissions on %s\n", progname
,
221 #endif /* CIFS_LEGACY_SETUID_CHECK */
226 #if CIFS_DISABLE_SETUID_CHECK
232 #else /* CIFS_DISABLE_SETUID_CHECK */
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");
247 #endif /* CIFS_DISABLE_SETUID_CHECK */
249 #if CIFS_LEGACY_SETUID_CHECK
251 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
256 #else /* CIFS_LEGACY_SETUID_CHECK */
258 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
264 /* make sure this mount is listed in /etc/fstab */
265 fstab
= setmntent(_PATH_FSTAB
, "r");
267 fprintf(stderr
, "Couldn't open %s for reading!\n",
272 while((mnt
= getmntent(fstab
))) {
273 if (!strcmp(mountpoint
, mnt
->mnt_dir
))
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
,
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.
292 *options
= strdup(mnt
->mnt_opts
);
295 #endif /* CIFS_LEGACY_SETUID_CHECK */
300 open nofollow - avoid symlink exposure?
301 get owner of dir see if matches self or if root
302 call system(umount argv) etc.
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
)
339 /* caller frees username if necessary */
340 static char * getusername(void) {
341 char *username
= NULL
;
342 struct passwd
*password
= getpwuid(getuid());
345 username
= password
->pw_name
;
350 static int open_cred_file(char * file_name
)
357 i
= access(file_name
, R_OK
);
361 fs
= fopen(file_name
,"r");
364 line_buf
= (char *)malloc(4096);
365 if(line_buf
== NULL
) {
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'))
377 /* if whitespace - skip past it */
379 if (strncasecmp("username",line_buf
+i
,8) == 0) {
380 temp_val
= strchr(line_buf
+ i
,'=');
382 /* go past equals sign */
384 for(length
= 0;length
<4087;length
++) {
385 if ((temp_val
[length
] == '\n')
386 || (temp_val
[length
] == '\0')) {
387 temp_val
[length
] = '\0';
392 fprintf(stderr
, "mount.cifs failed due to malformed username in credentials file\n");
393 memset(line_buf
,0,4096);
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
,'=');
406 /* go past equals sign */
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';
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);
420 if(mountpassword
== NULL
) {
421 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
423 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
425 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
430 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
431 temp_val
= strchr(line_buf
+i
,'=');
433 /* go past equals sign */
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';
444 if(length
> DOMAIN_SIZE
) {
445 fprintf(stderr
, "mount.cifs failed: domain in credentials file too long\n");
448 if(domain_name
== NULL
) {
449 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
451 memset(domain_name
,0,DOMAIN_SIZE
);
453 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
466 static int get_password_from_file(int file_descript
, char * filename
)
472 if(mountpassword
== NULL
)
473 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
475 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
477 if (mountpassword
== NULL
) {
478 fprintf(stderr
, "malloc failed\n");
482 if(filename
!= NULL
) {
483 rc
= access(filename
, R_OK
);
485 fprintf(stderr
, "mount.cifs failed: access check of %s failed: %s\n",
486 filename
, strerror(errno
));
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
);
496 /* else file already open and fd provided */
498 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
499 rc
= read(file_descript
,&c
,1);
501 fprintf(stderr
, "mount.cifs failed. Error %s reading password file\n",strerror(errno
));
503 close(file_descript
);
506 if(mountpassword
[0] == 0) {
508 fprintf(stderr
, "\nWarning: null password used since cifs password file empty");
511 } else /* read valid character */ {
512 if((c
== 0) || (c
== '\n')) {
513 mountpassword
[i
] = '\0';
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",
524 if(filename
!= NULL
) {
525 close(file_descript
);
531 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
534 char * percent_char
= NULL
;
536 char * next_keyword
= NULL
;
544 if (!optionsp
|| !*optionsp
)
548 /* BB fixme check for separator override BB */
552 snprintf(user
,sizeof(user
),"%u",getuid());
554 snprintf(group
,sizeof(group
),"%u",getgid());
557 /* while ((data = strsep(&options, ",")) != NULL) { */
558 while(data
!= NULL
) {
559 /* check if ends with trailing comma */
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 */
573 /* temporarily null terminate keyword to make keyword and value distinct */
574 if ((value
= strchr(data
, '=')) != NULL
) {
579 if (strncmp(data
, "users",5) == 0) {
580 if(!value
|| !*value
) {
581 *filesys_flags
|= MS_USERS
;
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
;
593 fprintf(stderr
, "username specified with no parameter\n");
595 return 1; /* needs_arg; */
598 if (strnlen(value
, 260) < 260) {
600 percent_char
= strchr(value
,'%');
603 if(mountpassword
== NULL
)
604 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
607 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
610 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
611 /* remove password from username */
612 while(*percent_char
!= 0) {
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
624 domain_name
= check_for_domain(&value
);
626 fprintf(stderr
, "username too long\n");
631 } else if (strncmp(data
, "pass", 4) == 0) {
632 if (!value
|| !*value
) {
634 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
637 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
639 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
641 mountpassword
= strndup(value
, MOUNT_PASSWD_SIZE
);
642 if (!mountpassword
) {
643 fprintf(stderr
, "mount.cifs error: %s", strerror(ENOMEM
));
650 fprintf(stderr
, "password too long\n");
655 } else if (strncmp(data
, "sec", 3) == 0) {
657 if (!strncmp(value
, "none", 4) ||
658 !strncmp(value
, "krb5", 4))
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
) {
666 fprintf(stderr
, "ip address %s override specified\n",value
);
669 fprintf(stderr
, "ip address too long\n");
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");
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) {
686 if (strncmp(value
, "//", 2) == 0) {
688 fprintf(stderr
, "unc name specified twice, ignoring second\n");
691 } else if (strncmp(value
, "\\\\", 2) != 0) {
692 fprintf(stderr
, "UNC Path does not begin with // or \\\\ \n");
697 fprintf(stderr
, "unc name specified twice, ignoring second\n");
702 fprintf(stderr
, "CIFS: UNC name too long\n");
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");
714 return 1; /* needs_arg; */
716 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
719 fprintf(stderr
, "domain name too long\n");
723 } else if (strncmp(data
, "cred", 4) == 0) {
724 if (value
&& *value
) {
725 rc
= open_cred_file(value
);
727 fprintf(stderr
, "error %d (%s) opening credential file %s\n",
728 rc
, strerror(rc
), value
);
733 fprintf(stderr
, "invalid credential file name specified\n");
737 } else if (strncmp(data
, "uid", 3) == 0) {
738 if (value
&& *value
) {
740 if (!isdigit(*value
)) {
743 if (!(pw
= getpwnam(value
))) {
744 fprintf(stderr
, "bad user name \"%s\"\n", value
);
747 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
749 strlcpy(user
,value
,sizeof(user
));
753 } else if (strncmp(data
, "gid", 3) == 0) {
754 if (value
&& *value
) {
756 if (!isdigit(*value
)) {
759 if (!(gr
= getgrnam(value
))) {
760 fprintf(stderr
, "bad group name \"%s\"\n", value
);
763 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
765 strlcpy(group
,value
,sizeof(group
));
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
);
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
);
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");
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);
825 } else if (strncmp(data
, "ro", 2) == 0) {
826 *filesys_flags
|= MS_RDONLY
;
828 } else if (strncmp(data
, "rw", 2) == 0) {
829 *filesys_flags
&= ~MS_RDONLY
;
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) {
836 simple_strtoul(value, &value, 0);
838 } else if (strnicmp(data, "rsize", 5) == 0) {
839 if (value && *value) {
841 simple_strtoul(value, &value, 0);
843 } else if (strnicmp(data, "wsize", 5) == 0) {
844 if (value && *value) {
846 simple_strtoul(value, &value, 0);
848 } else if (strnicmp(data, "version", 3) == 0) {
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
);
857 word_len
+= 1 + strlen(value
);
859 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
866 strlcat(out
, ",", out_len
+ word_len
+ 2);
871 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
873 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
874 out_len
= strlen(out
);
880 /* special-case the uid and gid */
882 word_len
= strlen(user
);
884 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
891 strlcat(out
, ",", out_len
+ word_len
+ 6);
894 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
895 out_len
= strlen(out
);
898 word_len
= strlen(group
);
900 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
907 strlcat(out
, ",", out_len
+ word_len
+ 6);
910 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
911 out_len
= strlen(out
);
914 SAFE_FREE(*optionsp
);
919 /* replace all (one or more) commas with double commas */
920 static void check_for_comma(char ** ppasswrd
)
925 int number_of_commas
= 0;
940 if(number_of_commas
== 0)
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");
948 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
949 if(new_pass_buf
== NULL
)
952 for(i
=0,j
=0;i
<len
;i
++,j
++) {
953 new_pass_buf
[j
] = pass
[i
];
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
;
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
;
983 original_string
= *ppuser
;
985 if (original_string
== NULL
)
988 original_len
= strlen(original_string
);
990 usernm
= strchr(*ppuser
,'/');
991 if (usernm
== NULL
) {
992 usernm
= strchr(*ppuser
,'\\');
998 fprintf(stderr
, "Domain name specified twice. Username probably malformed\n");
1004 if (domainnm
[0] != 0) {
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
)
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 */
1026 for(i
=0;i
<original_len
;i
++) {
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,'\\')
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
;
1046 string
= strchr(string
, from
);
1049 if (string
>= lastchar
)
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
);
1062 struct addrinfo
*addrlist
;
1065 if(length
> (MAX_UNC_LEN
- 1)) {
1066 fprintf(stderr
, "mount error: UNC name too long");
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
);
1076 /* BB add code to find DFS root here */
1077 fprintf(stderr
, "\nMounting the DFS root for domain not implemented yet\n");
1080 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1081 /* check for nfs syntax ie server:share */
1082 share
= strchr(unc_name
,':');
1084 *punc_name
= (char *)malloc(length
+3);
1085 if(*punc_name
== NULL
) {
1086 /* put the original string back if
1088 *punc_name
= unc_name
;
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
;
1098 fprintf(stderr
, "mount error: improperly formatted UNC name.");
1099 fprintf(stderr
, " %s does not begin with \\\\ or //\n",unc_name
);
1103 continue_unc_parsing
:
1108 /* allow for either delimiter between host and sharename */
1109 if ((share
= strpbrk(unc_name
, "/\\"))) {
1110 *share
= 0; /* temporarily terminate the string */
1113 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1115 fprintf(stderr
, "mount error: could not resolve address for %s: %s\n",
1116 unc_name
, gai_strerror(rc
));
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 */
1130 fprintf(stderr
, "ip address specified explicitly\n");
1133 /* BB should we pass an alternate version of the share name as Unicode */
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");
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 */
1177 uppercase_string(char *string
)
1183 /* check for unicode */
1184 if ((unsigned char) string
[0] & 0x80)
1186 *string
= toupper((unsigned char) *string
);
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
)
1211 for (s
= "\n"; *s
; s
++) {
1212 if (strchr(name
, *s
)) {
1213 fprintf(stderr
, "%s: illegal character 0x%02x in mount entry\n",
1221 static int check_mtab(const char *progname
, const char *devname
,
1224 if (check_newline(progname
, devname
) == -1 ||
1225 check_newline(progname
, dir
) == -1)
1231 int main(int argc
, char ** argv
)
1234 unsigned long flags
= MS_MANDLOCK
;
1235 char * orgoptions
= NULL
;
1236 char * share_name
= NULL
;
1237 const char * ipaddr
= NULL
;
1239 char * mountpoint
= NULL
;
1240 char * options
= NULL
;
1242 char * resolved_path
= NULL
;
1253 size_t options_size
= 0;
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
;
1266 /* setlocale(LC_ALL, "");
1267 bindtextdomain(PACKAGE, LOCALEDIR);
1268 textdomain(PACKAGE); */
1271 thisprogram
= argv
[0];
1273 mount_cifs_usage(stderr
);
1275 if(thisprogram
== NULL
)
1276 thisprogram
= "mount.cifs";
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);
1286 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1287 if (share_name
== NULL
) {
1288 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
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();
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
);
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) {
1315 /* No code to do the following options yet */
1317 list_with_volumelabel = 1;
1320 volumelabel = optarg;
1327 case 'h': /* help */
1328 mount_cifs_usage(stdout
);
1337 "option 'b' (MS_BIND) not supported\n");
1345 "option 'm' (MS_MOVE) not supported\n");
1349 orgoptions
= strdup(optarg
);
1351 case 'r': /* mount readonly */
1361 print_cifs_mount_version();
1364 flags
&= ~MS_RDONLY
;
1367 rsize
= atoi(optarg
) ;
1370 wsize
= atoi(optarg
);
1373 if (isdigit(*optarg
)) {
1376 uid
= strtoul(optarg
, &ep
, 10);
1378 fprintf(stderr
, "bad uid value \"%s\"\n", optarg
);
1384 if (!(pw
= getpwnam(optarg
))) {
1385 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1393 if (isdigit(*optarg
)) {
1396 gid
= strtoul(optarg
, &ep
, 10);
1398 fprintf(stderr
, "bad gid value \"%s\"\n", optarg
);
1404 if (!(gr
= getgrnam(optarg
))) {
1405 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1417 domain_name
= optarg
; /* BB fix this - currently ignored */
1421 if(mountpassword
== NULL
)
1422 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1425 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1429 get_password_from_file(0 /* stdin */,NULL
);
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
);
1449 fprintf(stderr
, "Couldn't chdir to %s: %s\n", mountpoint
,
1455 rc
= check_mountpoint(thisprogram
, mountpoint
);
1459 /* sanity check for unprivileged mounts */
1461 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
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);
1474 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+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
)) {
1489 #if !CIFS_LEGACY_SETUID_CHECK
1490 if (!(flags
& (MS_USERS
|MS_USER
))) {
1491 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1495 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1498 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1499 "CIFS mounts not supported.",
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");
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");
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
));
1532 mountpoint
= resolved_path
;
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 */
1539 user_name
= strdup(getenv("USER"));
1540 if (user_name
== NULL
)
1541 user_name
= getusername();
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");
1553 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1556 /* FIXME launch daemon (handles dfs name resolution and credential change)
1557 remember to clear parms and overwrite password field before launching */
1559 optlen
= strlen(orgoptions
);
1564 optlen
+= strlen(share_name
) + 4;
1566 fprintf(stderr
, "No server share name specified\n");
1567 fprintf(stderr
, "\nMounting the DFS root for server not implemented yet\n");
1571 optlen
+= strlen(user_name
) + 6;
1572 optlen
+= MAX_ADDRESS_LEN
+ 4;
1574 optlen
+= strlen(mountpassword
) + 6;
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");
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)
1592 /* check for syntax like user=domain\user */
1594 domain_name
= check_for_domain(&user_name
);
1595 strlcat(options
,",user=",options_size
);
1596 strlcat(options
,user_name
,options_size
);
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
);
1610 strlcat(options
,",",options_size
);
1611 strlcat(options
,orgoptions
,options_size
);
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
) {
1627 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1628 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1629 options_size
- current_len
);
1632 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1633 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1634 options_size
- current_len
);
1640 /* if the address looks bogus, try the next one */
1642 addr
= addr
->ai_next
;
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
);
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
1667 check_for_comma(&mountpassword
);
1668 strlcat(options
,",pass=",options_size
);
1669 strlcat(options
,mountpassword
,options_size
);
1671 fprintf(stderr
, ",pass=********");
1675 fprintf(stderr
, "\n");
1677 rc
= check_mtab(thisprogram
, dev_name
, mountpoint
);
1681 if (!fakemnt
&& mount(dev_name
, ".", cifs_fstype
, flags
, options
)) {
1686 addr
= addr
->ai_next
;
1692 fprintf(stderr
, "mount error: cifs filesystem not supported by the system\n");
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");
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 "
1714 atexit(unlock_mtab
);
1717 fprintf(stderr
, "cannot lock mtab");
1720 pmntfile
= setmntent(MOUNTED
, "a+");
1722 fprintf(stderr
, "could not update mount table\n");
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);
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);
1750 strlcat(mountent
.mnt_opts
,
1752 strlcat(mountent
.mnt_opts
,
1757 mountent
.mnt_freq
= 0;
1758 mountent
.mnt_passno
= 0;
1759 rc
= addmntent(pmntfile
,&mountent
);
1760 endmntent(pmntfile
);
1762 SAFE_FREE(mountent
.mnt_opts
);
1767 int len
= strlen(mountpassword
);
1768 memset(mountpassword
,0,len
);
1769 SAFE_FREE(mountpassword
);
1773 freeaddrinfo(addrhead
);
1775 SAFE_FREE(orgoptions
);
1776 SAFE_FREE(resolved_path
);
1777 SAFE_FREE(share_name
);