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 "13"
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
79 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
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 * By default, mount.cifs follows the conventions set forth by /bin/mount
93 * for user mounts. That is, it requires that the mount be listed in
94 * /etc/fstab with the "user" option when run as an unprivileged user and
95 * mount.cifs is setuid root.
97 * Older versions of mount.cifs however were "looser" in this regard. When
98 * made setuid root, a user could run mount.cifs directly and mount any share
99 * on a directory owned by that user.
101 * The legacy behavior is now disabled by default. To reenable it, set the
102 * following #define to true.
104 #define CIFS_LEGACY_SETUID_CHECK 0
107 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
108 * flags by default. These defaults can be changed here.
110 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
112 const char *thisprogram
;
115 static int got_password
= 0;
116 static int got_user
= 0;
117 static int got_domain
= 0;
118 static int got_ip
= 0;
119 static int got_unc
= 0;
120 static int got_uid
= 0;
121 static int got_gid
= 0;
122 static char * user_name
= NULL
;
123 static char * mountpassword
= NULL
;
124 char * domain_name
= NULL
;
125 char * prefixpath
= NULL
;
127 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
128 * don't link to libreplace so need them here. */
130 /* like strncpy but does not 0 fill the buffer and always null
131 * terminates. bufsize is the size of the destination buffer */
134 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
136 size_t len
= strlen(s
);
138 if (bufsize
<= 0) return 0;
139 if (len
>= bufsize
) len
= bufsize
-1;
146 /* like strncat but does not 0 fill the buffer and always null
147 * terminates. bufsize is the length of the buffer, which should
148 * be one more than the maximum resulting string length */
151 static size_t strlcat(char *d
, const char *s
, size_t bufsize
)
153 size_t len1
= strlen(d
);
154 size_t len2
= strlen(s
);
155 size_t ret
= len1
+ len2
;
157 if (len1
+len2
>= bufsize
) {
158 if (bufsize
< (len1
+1)) {
161 len2
= bufsize
- (len1
+1);
164 memcpy(d
+len1
, s
, len2
);
172 * If an unprivileged user is doing the mounting then we need to ensure
173 * that the entry is in /etc/fstab.
176 check_mountpoint(const char *progname
, char *mountpoint
)
181 /* does mountpoint exist and is it a directory? */
182 err
= stat(mountpoint
, &statbuf
);
184 fprintf(stderr
, "%s: failed to stat %s: %s\n", progname
,
185 mountpoint
, strerror(errno
));
189 if (!S_ISDIR(statbuf
.st_mode
)) {
190 fprintf(stderr
, "%s: %s is not a directory!", progname
,
195 #if CIFS_LEGACY_SETUID_CHECK
196 /* do extra checks on mountpoint for legacy setuid behavior */
197 if (!getuid() || geteuid())
200 if (statbuf
.st_uid
!= getuid()) {
201 fprintf(stderr
, "%s: %s is not owned by user\n", progname
,
206 if ((statbuf
.st_mode
& S_IRWXU
) != S_IRWXU
) {
207 fprintf(stderr
, "%s: invalid permissions on %s\n", progname
,
211 #endif /* CIFS_LEGACY_SETUID_CHECK */
216 #if CIFS_LEGACY_SETUID_CHECK
218 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
223 #else /* CIFS_LEGACY_SETUID_CHECK */
225 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
231 /* make sure this mount is listed in /etc/fstab */
232 fstab
= setmntent(_PATH_FSTAB
, "r");
234 fprintf(stderr
, "Couldn't open %s for reading!\n",
239 while((mnt
= getmntent(fstab
))) {
240 if (!strcmp(mountpoint
, mnt
->mnt_dir
))
245 if (mnt
== NULL
|| strcmp(mnt
->mnt_fsname
, devname
)) {
246 fprintf(stderr
, "%s: permission denied: no match for "
247 "%s found in %s\n", progname
, mountpoint
,
253 * 'mount' munges the options from fstab before passing them
254 * to us. It is non-trivial to test that we have the correct
255 * set of options. We don't want to trust what the user
256 * gave us, so just take whatever is in /etc/fstab.
259 *options
= strdup(mnt
->mnt_opts
);
262 #endif /* CIFS_LEGACY_SETUID_CHECK */
267 open nofollow - avoid symlink exposure?
268 get owner of dir see if matches self or if root
269 call system(umount argv) etc.
273 static char * check_for_domain(char **);
276 static void mount_cifs_usage(FILE *stream
)
278 fprintf(stream
, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
279 fprintf(stream
, "\nMount the remote target, specified as a UNC name,");
280 fprintf(stream
, " to a local directory.\n\nOptions:\n");
281 fprintf(stream
, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282 fprintf(stream
, "\nLess commonly used options:");
283 fprintf(stream
, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284 fprintf(stream
, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285 fprintf(stream
, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286 fprintf(stream
, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287 fprintf(stream
, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
288 fprintf(stream
, "\n\t(e.g. unneeded for mounts to most Samba versions):");
289 fprintf(stream
, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290 fprintf(stream
, "\n\nRarely used options:");
291 fprintf(stream
, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292 fprintf(stream
, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293 fprintf(stream
, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 fprintf(stream
, "\n\nOptions are described in more detail in the manual page");
295 fprintf(stream
, "\n\tman 8 mount.cifs\n");
296 fprintf(stream
, "\nTo display the version number of the mount helper:");
297 fprintf(stream
, "\n\t%s -V\n",thisprogram
);
299 SAFE_FREE(mountpassword
);
301 if (stream
== stderr
)
306 /* caller frees username if necessary */
307 static char * getusername(void) {
308 char *username
= NULL
;
309 struct passwd
*password
= getpwuid(getuid());
312 username
= password
->pw_name
;
317 static int open_cred_file(char * file_name
)
324 i
= access(file_name
, R_OK
);
328 fs
= fopen(file_name
,"r");
331 line_buf
= (char *)malloc(4096);
332 if(line_buf
== NULL
) {
337 while(fgets(line_buf
,4096,fs
)) {
338 /* parse line from credential file */
340 /* eat leading white space */
341 for(i
=0;i
<4086;i
++) {
342 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
344 /* if whitespace - skip past it */
346 if (strncasecmp("username",line_buf
+i
,8) == 0) {
347 temp_val
= strchr(line_buf
+ i
,'=');
349 /* go past equals sign */
351 for(length
= 0;length
<4087;length
++) {
352 if ((temp_val
[length
] == '\n')
353 || (temp_val
[length
] == '\0')) {
354 temp_val
[length
] = '\0';
359 fprintf(stderr
, "mount.cifs failed due to malformed username in credentials file\n");
360 memset(line_buf
,0,4096);
364 user_name
= (char *)calloc(1 + length
,1);
365 /* BB adding free of user_name string before exit,
366 not really necessary but would be cleaner */
367 strlcpy(user_name
,temp_val
, length
+1);
370 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
371 temp_val
= strchr(line_buf
+i
,'=');
373 /* go past equals sign */
375 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
376 if ((temp_val
[length
] == '\n')
377 || (temp_val
[length
] == '\0')) {
378 temp_val
[length
] = '\0';
382 if(length
> MOUNT_PASSWD_SIZE
) {
383 fprintf(stderr
, "mount.cifs failed: password in credentials file too long\n");
384 memset(line_buf
,0, 4096);
387 if(mountpassword
== NULL
) {
388 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
390 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
392 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
397 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
398 temp_val
= strchr(line_buf
+i
,'=');
400 /* go past equals sign */
403 fprintf(stderr
, "\nDomain %s\n",temp_val
);
404 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
405 if ((temp_val
[length
] == '\n')
406 || (temp_val
[length
] == '\0')) {
407 temp_val
[length
] = '\0';
411 if(length
> DOMAIN_SIZE
) {
412 fprintf(stderr
, "mount.cifs failed: domain in credentials file too long\n");
415 if(domain_name
== NULL
) {
416 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
418 memset(domain_name
,0,DOMAIN_SIZE
);
420 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
433 static int get_password_from_file(int file_descript
, char * filename
)
439 if(mountpassword
== NULL
)
440 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
442 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
444 if (mountpassword
== NULL
) {
445 fprintf(stderr
, "malloc failed\n");
449 if(filename
!= NULL
) {
450 rc
= access(filename
, R_OK
);
452 fprintf(stderr
, "mount.cifs failed: access check of %s failed: %s\n",
453 filename
, strerror(errno
));
456 file_descript
= open(filename
, O_RDONLY
);
457 if(file_descript
< 0) {
458 fprintf(stderr
, "mount.cifs failed. %s attempting to open password file %s\n",
459 strerror(errno
),filename
);
463 /* else file already open and fd provided */
465 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
466 rc
= read(file_descript
,&c
,1);
468 fprintf(stderr
, "mount.cifs failed. Error %s reading password file\n",strerror(errno
));
470 close(file_descript
);
473 if(mountpassword
[0] == 0) {
475 fprintf(stderr
, "\nWarning: null password used since cifs password file empty");
478 } else /* read valid character */ {
479 if((c
== 0) || (c
== '\n')) {
480 mountpassword
[i
] = '\0';
483 mountpassword
[i
] = c
;
486 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
487 fprintf(stderr
, "\nWarning: password longer than %d characters specified in cifs password file",
491 if(filename
!= NULL
) {
492 close(file_descript
);
498 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
501 char * percent_char
= NULL
;
503 char * next_keyword
= NULL
;
511 if (!optionsp
|| !*optionsp
)
515 /* BB fixme check for separator override BB */
519 snprintf(user
,sizeof(user
),"%u",getuid());
521 snprintf(group
,sizeof(group
),"%u",getgid());
524 /* while ((data = strsep(&options, ",")) != NULL) { */
525 while(data
!= NULL
) {
526 /* check if ends with trailing comma */
530 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
531 /* data = next keyword */
532 /* value = next value ie stuff after equal sign */
534 next_keyword
= strchr(data
,','); /* BB handle sep= */
536 /* temporarily null terminate end of keyword=value pair */
540 /* temporarily null terminate keyword to make keyword and value distinct */
541 if ((value
= strchr(data
, '=')) != NULL
) {
546 if (strncmp(data
, "users",5) == 0) {
547 if(!value
|| !*value
) {
548 *filesys_flags
|= MS_USERS
;
551 } else if (strncmp(data
, "user_xattr",10) == 0) {
552 /* do nothing - need to skip so not parsed as user name */
553 } else if (strncmp(data
, "user", 4) == 0) {
555 if (!value
|| !*value
) {
556 if(data
[4] == '\0') {
557 *filesys_flags
|= MS_USER
;
560 fprintf(stderr
, "username specified with no parameter\n");
562 return 1; /* needs_arg; */
565 if (strnlen(value
, 260) < 260) {
567 percent_char
= strchr(value
,'%');
570 if(mountpassword
== NULL
)
571 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
574 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
577 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
578 /* remove password from username */
579 while(*percent_char
!= 0) {
585 /* this is only case in which the user
586 name buf is not malloc - so we have to
587 check for domain name embedded within
588 the user name here since the later
589 call to check_for_domain will not be
591 domain_name
= check_for_domain(&value
);
593 fprintf(stderr
, "username too long\n");
598 } else if (strncmp(data
, "pass", 4) == 0) {
599 if (!value
|| !*value
) {
601 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
604 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
606 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
608 mountpassword
= strndup(value
, MOUNT_PASSWD_SIZE
);
609 if (!mountpassword
) {
610 fprintf(stderr
, "mount.cifs error: %s", strerror(ENOMEM
));
617 fprintf(stderr
, "password too long\n");
622 } else if (strncmp(data
, "sec", 3) == 0) {
624 if (!strncmp(value
, "none", 4) ||
625 !strncmp(value
, "krb5", 4))
628 } else if (strncmp(data
, "ip", 2) == 0) {
629 if (!value
|| !*value
) {
630 fprintf(stderr
, "target ip address argument missing");
631 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
633 fprintf(stderr
, "ip address %s override specified\n",value
);
636 fprintf(stderr
, "ip address too long\n");
640 } else if ((strncmp(data
, "unc", 3) == 0)
641 || (strncmp(data
, "target", 6) == 0)
642 || (strncmp(data
, "path", 4) == 0)) {
643 if (!value
|| !*value
) {
644 fprintf(stderr
, "invalid path to network resource\n");
646 return 1; /* needs_arg; */
647 } else if(strnlen(value
,5) < 5) {
648 fprintf(stderr
, "UNC name too short");
651 if (strnlen(value
, 300) < 300) {
653 if (strncmp(value
, "//", 2) == 0) {
655 fprintf(stderr
, "unc name specified twice, ignoring second\n");
658 } else if (strncmp(value
, "\\\\", 2) != 0) {
659 fprintf(stderr
, "UNC Path does not begin with // or \\\\ \n");
664 fprintf(stderr
, "unc name specified twice, ignoring second\n");
669 fprintf(stderr
, "CIFS: UNC name too long\n");
673 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
674 || (strncmp(data
, "workg", 5) == 0)) {
675 /* note this allows for synonyms of "domain"
676 such as "DOM" and "dom" and "workgroup"
677 and "WORKGRP" etc. */
678 if (!value
|| !*value
) {
679 fprintf(stderr
, "CIFS: invalid domain name\n");
681 return 1; /* needs_arg; */
683 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
686 fprintf(stderr
, "domain name too long\n");
690 } else if (strncmp(data
, "cred", 4) == 0) {
691 if (value
&& *value
) {
692 rc
= open_cred_file(value
);
694 fprintf(stderr
, "error %d (%s) opening credential file %s\n",
695 rc
, strerror(rc
), value
);
700 fprintf(stderr
, "invalid credential file name specified\n");
704 } else if (strncmp(data
, "uid", 3) == 0) {
705 if (value
&& *value
) {
707 if (!isdigit(*value
)) {
710 if (!(pw
= getpwnam(value
))) {
711 fprintf(stderr
, "bad user name \"%s\"\n", value
);
714 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
716 strlcpy(user
,value
,sizeof(user
));
720 } else if (strncmp(data
, "gid", 3) == 0) {
721 if (value
&& *value
) {
723 if (!isdigit(*value
)) {
726 if (!(gr
= getgrnam(value
))) {
727 fprintf(stderr
, "bad group name \"%s\"\n", value
);
730 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
732 strlcpy(group
,value
,sizeof(group
));
736 /* fmask and dmask synonyms for people used to smbfs syntax */
737 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
738 if (!value
|| !*value
) {
739 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
744 if (value
[0] != '0') {
745 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
748 if (strcmp (data
, "fmask") == 0) {
749 fprintf(stderr
, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
750 data
= "file_mode"; /* BB fix this */
752 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
753 if (!value
|| !*value
) {
754 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
759 if (value
[0] != '0') {
760 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
763 if (strcmp (data
, "dmask") == 0) {
764 fprintf(stderr
, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
767 /* the following eight mount options should be
768 stripped out from what is passed into the kernel
769 since these eight options are best passed as the
770 mount flags rather than redundantly to the kernel
771 and could generate spurious warnings depending on the
772 level of the corresponding cifs vfs kernel code */
773 } else if (strncmp(data
, "nosuid", 6) == 0) {
774 *filesys_flags
|= MS_NOSUID
;
775 } else if (strncmp(data
, "suid", 4) == 0) {
776 *filesys_flags
&= ~MS_NOSUID
;
777 } else if (strncmp(data
, "nodev", 5) == 0) {
778 *filesys_flags
|= MS_NODEV
;
779 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
780 (strncmp(data
, "nolock", 6) == 0)) {
781 *filesys_flags
&= ~MS_MANDLOCK
;
782 } else if (strncmp(data
, "dev", 3) == 0) {
783 *filesys_flags
&= ~MS_NODEV
;
784 } else if (strncmp(data
, "noexec", 6) == 0) {
785 *filesys_flags
|= MS_NOEXEC
;
786 } else if (strncmp(data
, "exec", 4) == 0) {
787 *filesys_flags
&= ~MS_NOEXEC
;
788 } else if (strncmp(data
, "guest", 5) == 0) {
789 user_name
= (char *)calloc(1, 1);
792 } else if (strncmp(data
, "ro", 2) == 0) {
793 *filesys_flags
|= MS_RDONLY
;
795 } else if (strncmp(data
, "rw", 2) == 0) {
796 *filesys_flags
&= ~MS_RDONLY
;
798 } else if (strncmp(data
, "remount", 7) == 0) {
799 *filesys_flags
|= MS_REMOUNT
;
800 } /* else if (strnicmp(data, "port", 4) == 0) {
801 if (value && *value) {
803 simple_strtoul(value, &value, 0);
805 } else if (strnicmp(data, "rsize", 5) == 0) {
806 if (value && *value) {
808 simple_strtoul(value, &value, 0);
810 } else if (strnicmp(data, "wsize", 5) == 0) {
811 if (value && *value) {
813 simple_strtoul(value, &value, 0);
815 } else if (strnicmp(data, "version", 3) == 0) {
817 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
818 } */ /* nothing to do on those four mount options above.
819 Just pass to kernel and ignore them here */
821 /* Copy (possibly modified) option to out */
822 word_len
= strlen(data
);
824 word_len
+= 1 + strlen(value
);
826 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
833 strlcat(out
, ",", out_len
+ word_len
+ 2);
838 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
840 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
841 out_len
= strlen(out
);
847 /* special-case the uid and gid */
849 word_len
= strlen(user
);
851 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
858 strlcat(out
, ",", out_len
+ word_len
+ 6);
861 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
862 out_len
= strlen(out
);
865 word_len
= strlen(group
);
867 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
874 strlcat(out
, ",", out_len
+ word_len
+ 6);
877 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
878 out_len
= strlen(out
);
881 SAFE_FREE(*optionsp
);
886 /* replace all (one or more) commas with double commas */
887 static void check_for_comma(char ** ppasswrd
)
892 int number_of_commas
= 0;
907 if(number_of_commas
== 0)
909 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
910 /* would otherwise overflow the mount options buffer */
911 fprintf(stderr
, "\nInvalid password. Password contains too many commas.\n");
915 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
916 if(new_pass_buf
== NULL
)
919 for(i
=0,j
=0;i
<len
;i
++,j
++) {
920 new_pass_buf
[j
] = pass
[i
];
923 new_pass_buf
[j
] = pass
[i
];
926 new_pass_buf
[len
+number_of_commas
] = 0;
928 SAFE_FREE(*ppasswrd
);
929 *ppasswrd
= new_pass_buf
;
934 /* Usernames can not have backslash in them and we use
935 [BB check if usernames can have forward slash in them BB]
936 backslash as domain\user separator character
938 static char * check_for_domain(char **ppuser
)
940 char * original_string
;
950 original_string
= *ppuser
;
952 if (original_string
== NULL
)
955 original_len
= strlen(original_string
);
957 usernm
= strchr(*ppuser
,'/');
958 if (usernm
== NULL
) {
959 usernm
= strchr(*ppuser
,'\\');
965 fprintf(stderr
, "Domain name specified twice. Username probably malformed\n");
971 if (domainnm
[0] != 0) {
974 fprintf(stderr
, "null domain\n");
976 len
= strlen(domainnm
);
977 /* reset domainm to new buffer, and copy
978 domain name into it */
979 domainnm
= (char *)malloc(len
+1);
983 strlcpy(domainnm
,*ppuser
,len
+1);
985 /* move_string(*ppuser, usernm+1) */
986 len
= strlen(usernm
+1);
988 if(len
>= original_len
) {
989 /* should not happen */
993 for(i
=0;i
<original_len
;i
++) {
995 original_string
[i
] = usernm
[i
+1];
996 else /* stuff with commas to remove last parm */
997 original_string
[i
] = ',';
1000 /* BB add check for more than one slash?
1001 strchr(*ppuser,'/');
1002 strchr(*ppuser,'\\')
1008 /* replace all occurances of "from" in a string with "to" */
1009 static void replace_char(char *string
, char from
, char to
, int maxlen
)
1011 char *lastchar
= string
+ maxlen
;
1013 string
= strchr(string
, from
);
1016 if (string
>= lastchar
)
1022 /* Note that caller frees the returned buffer if necessary */
1023 static struct addrinfo
*
1024 parse_server(char ** punc_name
)
1026 char * unc_name
= *punc_name
;
1027 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1029 struct addrinfo
*addrlist
;
1032 if(length
> (MAX_UNC_LEN
- 1)) {
1033 fprintf(stderr
, "mount error: UNC name too long");
1036 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1037 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1038 fprintf(stderr
, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1043 /* BB add code to find DFS root here */
1044 fprintf(stderr
, "\nMounting the DFS root for domain not implemented yet\n");
1047 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1048 /* check for nfs syntax ie server:share */
1049 share
= strchr(unc_name
,':');
1051 *punc_name
= (char *)malloc(length
+3);
1052 if(*punc_name
== NULL
) {
1053 /* put the original string back if
1055 *punc_name
= unc_name
;
1059 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1060 SAFE_FREE(unc_name
);
1061 unc_name
= *punc_name
;
1062 unc_name
[length
+2] = 0;
1063 goto continue_unc_parsing
;
1065 fprintf(stderr
, "mount error: improperly formatted UNC name.");
1066 fprintf(stderr
, " %s does not begin with \\\\ or //\n",unc_name
);
1070 continue_unc_parsing
:
1075 /* allow for either delimiter between host and sharename */
1076 if ((share
= strpbrk(unc_name
, "/\\"))) {
1077 *share
= 0; /* temporarily terminate the string */
1080 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1082 fprintf(stderr
, "mount error: could not resolve address for %s: %s\n",
1083 unc_name
, gai_strerror(rc
));
1087 *(share
- 1) = '/'; /* put delimiter back */
1089 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1090 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1091 *prefixpath
= 0; /* permanently terminate the string */
1092 if (!strlen(++prefixpath
))
1093 prefixpath
= NULL
; /* this needs to be done explicitly */
1097 fprintf(stderr
, "ip address specified explicitly\n");
1100 /* BB should we pass an alternate version of the share name as Unicode */
1104 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1105 fprintf(stderr
, "Mounting the DFS root for a particular server not implemented yet\n");
1112 static struct option longopts
[] = {
1113 { "all", 0, NULL
, 'a' },
1114 { "help",0, NULL
, 'h' },
1115 { "move",0, NULL
, 'm' },
1116 { "bind",0, NULL
, 'b' },
1117 { "read-only", 0, NULL
, 'r' },
1118 { "ro", 0, NULL
, 'r' },
1119 { "verbose", 0, NULL
, 'v' },
1120 { "version", 0, NULL
, 'V' },
1121 { "read-write", 0, NULL
, 'w' },
1122 { "rw", 0, NULL
, 'w' },
1123 { "options", 1, NULL
, 'o' },
1124 { "type", 1, NULL
, 't' },
1125 { "rsize",1, NULL
, 'R' },
1126 { "wsize",1, NULL
, 'W' },
1127 { "uid", 1, NULL
, '1'},
1128 { "gid", 1, NULL
, '2'},
1129 { "user",1,NULL
,'u'},
1130 { "username",1,NULL
,'u'},
1131 { "dom",1,NULL
,'d'},
1132 { "domain",1,NULL
,'d'},
1133 { "password",1,NULL
,'p'},
1134 { "pass",1,NULL
,'p'},
1135 { "credentials",1,NULL
,'c'},
1136 { "port",1,NULL
,'P'},
1137 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1138 { NULL
, 0, NULL
, 0 }
1141 /* convert a string to uppercase. return false if the string
1142 * wasn't ASCII. Return success on a NULL ptr */
1144 uppercase_string(char *string
)
1150 /* check for unicode */
1151 if ((unsigned char) string
[0] & 0x80)
1153 *string
= toupper((unsigned char) *string
);
1160 static void print_cifs_mount_version(void)
1162 printf("mount.cifs version: %s.%s%s\n",
1163 MOUNT_CIFS_VERSION_MAJOR
,
1164 MOUNT_CIFS_VERSION_MINOR
,
1165 MOUNT_CIFS_VENDOR_SUFFIX
);
1168 int main(int argc
, char ** argv
)
1171 unsigned long flags
= MS_MANDLOCK
;
1172 char * orgoptions
= NULL
;
1173 char * share_name
= NULL
;
1174 const char * ipaddr
= NULL
;
1176 char * mountpoint
= NULL
;
1177 char * options
= NULL
;
1179 char * resolved_path
= NULL
;
1190 size_t options_size
= 0;
1192 int retry
= 0; /* set when we have to retry mount with uppercase */
1193 struct addrinfo
*addrhead
= NULL
, *addr
;
1194 struct utsname sysinfo
;
1195 struct mntent mountent
;
1196 struct sockaddr_in
*addr4
;
1197 struct sockaddr_in6
*addr6
;
1200 /* setlocale(LC_ALL, "");
1201 bindtextdomain(PACKAGE, LOCALEDIR);
1202 textdomain(PACKAGE); */
1205 thisprogram
= argv
[0];
1207 mount_cifs_usage(stderr
);
1209 if(thisprogram
== NULL
)
1210 thisprogram
= "mount.cifs";
1213 /* BB add workstation name and domain and pass down */
1215 /* #ifdef _GNU_SOURCE
1216 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1220 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1221 if (share_name
== NULL
) {
1222 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1225 mountpoint
= argv
[2];
1226 } else if (argc
== 2) {
1227 if ((strcmp(argv
[1], "-V") == 0) ||
1228 (strcmp(argv
[1], "--version") == 0))
1230 print_cifs_mount_version();
1234 if ((strcmp(argv
[1], "-h") == 0) ||
1235 (strcmp(argv
[1], "-?") == 0) ||
1236 (strcmp(argv
[1], "--help") == 0))
1237 mount_cifs_usage(stdout
);
1239 mount_cifs_usage(stderr
);
1241 mount_cifs_usage(stderr
);
1245 /* add sharename in opts string as unc= parm */
1246 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1247 longopts
, NULL
)) != -1) {
1249 /* No code to do the following options yet */
1251 list_with_volumelabel = 1;
1254 volumelabel = optarg;
1261 case 'h': /* help */
1262 mount_cifs_usage(stdout
);
1271 "option 'b' (MS_BIND) not supported\n");
1279 "option 'm' (MS_MOVE) not supported\n");
1283 orgoptions
= strdup(optarg
);
1285 case 'r': /* mount readonly */
1295 print_cifs_mount_version();
1298 flags
&= ~MS_RDONLY
;
1301 rsize
= atoi(optarg
) ;
1304 wsize
= atoi(optarg
);
1307 if (isdigit(*optarg
)) {
1310 uid
= strtoul(optarg
, &ep
, 10);
1312 fprintf(stderr
, "bad uid value \"%s\"\n", optarg
);
1318 if (!(pw
= getpwnam(optarg
))) {
1319 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1327 if (isdigit(*optarg
)) {
1330 gid
= strtoul(optarg
, &ep
, 10);
1332 fprintf(stderr
, "bad gid value \"%s\"\n", optarg
);
1338 if (!(gr
= getgrnam(optarg
))) {
1339 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1351 domain_name
= optarg
; /* BB fix this - currently ignored */
1355 if(mountpassword
== NULL
)
1356 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1359 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1363 get_password_from_file(0 /* stdin */,NULL
);
1371 fprintf(stderr
, "unknown mount option %c\n",c
);
1372 mount_cifs_usage(stderr
);
1376 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1377 mount_cifs_usage(stderr
);
1380 /* make sure mountpoint is legit */
1381 rc
= check_mountpoint(thisprogram
, mountpoint
);
1385 /* sanity check for unprivileged mounts */
1387 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1392 /* enable any default user mount flags */
1393 flags
|= CIFS_SETUID_FLAGS
;
1396 if (getenv("PASSWD")) {
1397 if(mountpassword
== NULL
)
1398 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1400 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1403 } else if (getenv("PASSWD_FD")) {
1404 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1405 } else if (getenv("PASSWD_FILE")) {
1406 get_password_from_file(0, getenv("PASSWD_FILE"));
1409 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1415 #if !CIFS_LEGACY_SETUID_CHECK
1416 if (!(flags
& (MS_USERS
|MS_USER
))) {
1417 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1421 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1424 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1425 "CIFS mounts not supported.",
1432 flags
&= ~(MS_USERS
|MS_USER
);
1434 addrhead
= addr
= parse_server(&share_name
);
1435 if((addrhead
== NULL
) && (got_ip
== 0)) {
1436 fprintf(stderr
, "No ip address specified and hostname not found\n");
1441 /* BB save off path and pop after mount returns? */
1442 resolved_path
= (char *)malloc(PATH_MAX
+1);
1444 /* Note that if we can not canonicalize the name, we get
1445 another chance to see if it is valid when we chdir to it */
1446 if (realpath(mountpoint
, resolved_path
)) {
1447 mountpoint
= resolved_path
;
1451 /* Note that the password will not be retrieved from the
1452 USER env variable (ie user%password form) as there is
1453 already a PASSWD environment varaible */
1455 user_name
= strdup(getenv("USER"));
1456 if (user_name
== NULL
)
1457 user_name
= getusername();
1461 if(got_password
== 0) {
1462 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1463 no good replacement yet. */
1464 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1465 if (!tmp_pass
|| !mountpassword
) {
1466 fprintf(stderr
, "Password not entered, exiting\n");
1469 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1472 /* FIXME launch daemon (handles dfs name resolution and credential change)
1473 remember to clear parms and overwrite password field before launching */
1475 optlen
= strlen(orgoptions
);
1480 optlen
+= strlen(share_name
) + 4;
1482 fprintf(stderr
, "No server share name specified\n");
1483 fprintf(stderr
, "\nMounting the DFS root for server not implemented yet\n");
1487 optlen
+= strlen(user_name
) + 6;
1488 optlen
+= MAX_ADDRESS_LEN
+ 4;
1490 optlen
+= strlen(mountpassword
) + 6;
1493 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1494 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 */);
1496 if(options
== NULL
) {
1497 fprintf(stderr
, "Could not allocate memory for mount options\n");
1501 strlcpy(options
, "unc=", options_size
);
1502 strlcat(options
,share_name
,options_size
);
1503 /* scan backwards and reverse direction of slash */
1504 temp
= strrchr(options
, '/');
1505 if(temp
> options
+ 6)
1508 /* check for syntax like user=domain\user */
1510 domain_name
= check_for_domain(&user_name
);
1511 strlcat(options
,",user=",options_size
);
1512 strlcat(options
,user_name
,options_size
);
1516 /* extra length accounted for in option string above */
1517 strlcat(options
,",domain=",options_size
);
1518 strlcat(options
,domain_name
,options_size
);
1522 strlcat(options
,",ver=",options_size
);
1523 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1526 strlcat(options
,",",options_size
);
1527 strlcat(options
,orgoptions
,options_size
);
1530 strlcat(options
,",prefixpath=",options_size
);
1531 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1534 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1535 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1537 if (!got_ip
&& addr
) {
1538 strlcat(options
, ",ip=", options_size
);
1539 current_len
= strnlen(options
, options_size
);
1540 optionstail
= options
+ current_len
;
1541 switch (addr
->ai_addr
->sa_family
) {
1543 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1544 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1545 options_size
- current_len
);
1548 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1549 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1550 options_size
- current_len
);
1556 /* if the address looks bogus, try the next one */
1558 addr
= addr
->ai_next
;
1566 if (addr
->ai_addr
->sa_family
== AF_INET6
&& addr6
->sin6_scope_id
) {
1567 strlcat(options
, "%", options_size
);
1568 current_len
= strnlen(options
, options_size
);
1569 optionstail
= options
+ current_len
;
1570 snprintf(optionstail
, options_size
- current_len
, "%u",
1571 addr6
->sin6_scope_id
);
1575 fprintf(stderr
, "\nmount.cifs kernel mount options: %s", options
);
1577 if (mountpassword
) {
1579 * Commas have to be doubled, or else they will
1580 * look like the parameter separator
1583 check_for_comma(&mountpassword
);
1584 strlcat(options
,",pass=",options_size
);
1585 strlcat(options
,mountpassword
,options_size
);
1587 fprintf(stderr
, ",pass=********");
1591 fprintf(stderr
, "\n");
1593 if (!fakemnt
&& mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1598 addr
= addr
->ai_next
;
1604 fprintf(stderr
, "mount error: cifs filesystem not supported by the system\n");
1609 if (uppercase_string(dev_name
) &&
1610 uppercase_string(share_name
) &&
1611 uppercase_string(prefixpath
)) {
1612 fprintf(stderr
, "retrying with upper case share name\n");
1617 fprintf(stderr
, "mount error(%d): %s\n", errno
, strerror(errno
));
1618 fprintf(stderr
, "Refer to the mount.cifs(8) manual page (e.g. man "
1626 atexit(unlock_mtab
);
1629 fprintf(stderr
, "cannot lock mtab");
1632 pmntfile
= setmntent(MOUNTED
, "a+");
1634 fprintf(stderr
, "could not update mount table\n");
1639 mountent
.mnt_fsname
= dev_name
;
1640 mountent
.mnt_dir
= mountpoint
;
1641 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1642 mountent
.mnt_opts
= (char *)malloc(220);
1643 if(mountent
.mnt_opts
) {
1644 char * mount_user
= getusername();
1645 memset(mountent
.mnt_opts
,0,200);
1646 if(flags
& MS_RDONLY
)
1647 strlcat(mountent
.mnt_opts
,"ro",220);
1649 strlcat(mountent
.mnt_opts
,"rw",220);
1650 if(flags
& MS_MANDLOCK
)
1651 strlcat(mountent
.mnt_opts
,",mand",220);
1652 if(flags
& MS_NOEXEC
)
1653 strlcat(mountent
.mnt_opts
,",noexec",220);
1654 if(flags
& MS_NOSUID
)
1655 strlcat(mountent
.mnt_opts
,",nosuid",220);
1656 if(flags
& MS_NODEV
)
1657 strlcat(mountent
.mnt_opts
,",nodev",220);
1658 if(flags
& MS_SYNCHRONOUS
)
1659 strlcat(mountent
.mnt_opts
,",sync",220);
1662 strlcat(mountent
.mnt_opts
,
1664 strlcat(mountent
.mnt_opts
,
1669 mountent
.mnt_freq
= 0;
1670 mountent
.mnt_passno
= 0;
1671 rc
= addmntent(pmntfile
,&mountent
);
1672 endmntent(pmntfile
);
1674 SAFE_FREE(mountent
.mnt_opts
);
1679 int len
= strlen(mountpassword
);
1680 memset(mountpassword
,0,len
);
1681 SAFE_FREE(mountpassword
);
1685 freeaddrinfo(addrhead
);
1687 SAFE_FREE(orgoptions
);
1688 SAFE_FREE(resolved_path
);
1689 SAFE_FREE(share_name
);