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
)
323 fs
= fopen(file_name
,"r");
326 line_buf
= (char *)malloc(4096);
327 if(line_buf
== NULL
) {
332 while(fgets(line_buf
,4096,fs
)) {
333 /* parse line from credential file */
335 /* eat leading white space */
336 for(i
=0;i
<4086;i
++) {
337 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
339 /* if whitespace - skip past it */
341 if (strncasecmp("username",line_buf
+i
,8) == 0) {
342 temp_val
= strchr(line_buf
+ i
,'=');
344 /* go past equals sign */
346 for(length
= 0;length
<4087;length
++) {
347 if ((temp_val
[length
] == '\n')
348 || (temp_val
[length
] == '\0')) {
349 temp_val
[length
] = '\0';
354 fprintf(stderr
, "mount.cifs failed due to malformed username in credentials file\n");
355 memset(line_buf
,0,4096);
359 user_name
= (char *)calloc(1 + length
,1);
360 /* BB adding free of user_name string before exit,
361 not really necessary but would be cleaner */
362 strlcpy(user_name
,temp_val
, length
+1);
365 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
366 temp_val
= strchr(line_buf
+i
,'=');
368 /* go past equals sign */
370 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
371 if ((temp_val
[length
] == '\n')
372 || (temp_val
[length
] == '\0')) {
373 temp_val
[length
] = '\0';
377 if(length
> MOUNT_PASSWD_SIZE
) {
378 fprintf(stderr
, "mount.cifs failed: password in credentials file too long\n");
379 memset(line_buf
,0, 4096);
382 if(mountpassword
== NULL
) {
383 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
385 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
387 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
392 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
393 temp_val
= strchr(line_buf
+i
,'=');
395 /* go past equals sign */
398 fprintf(stderr
, "\nDomain %s\n",temp_val
);
399 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
400 if ((temp_val
[length
] == '\n')
401 || (temp_val
[length
] == '\0')) {
402 temp_val
[length
] = '\0';
406 if(length
> DOMAIN_SIZE
) {
407 fprintf(stderr
, "mount.cifs failed: domain in credentials file too long\n");
410 if(domain_name
== NULL
) {
411 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
413 memset(domain_name
,0,DOMAIN_SIZE
);
415 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
428 static int get_password_from_file(int file_descript
, char * filename
)
434 if(mountpassword
== NULL
)
435 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
437 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
439 if (mountpassword
== NULL
) {
440 fprintf(stderr
, "malloc failed\n");
444 if(filename
!= NULL
) {
445 file_descript
= open(filename
, O_RDONLY
);
446 if(file_descript
< 0) {
447 fprintf(stderr
, "mount.cifs failed. %s attempting to open password file %s\n",
448 strerror(errno
),filename
);
452 /* else file already open and fd provided */
454 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
455 rc
= read(file_descript
,&c
,1);
457 fprintf(stderr
, "mount.cifs failed. Error %s reading password file\n",strerror(errno
));
459 close(file_descript
);
462 if(mountpassword
[0] == 0) {
464 fprintf(stderr
, "\nWarning: null password used since cifs password file empty");
467 } else /* read valid character */ {
468 if((c
== 0) || (c
== '\n')) {
469 mountpassword
[i
] = '\0';
472 mountpassword
[i
] = c
;
475 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
476 fprintf(stderr
, "\nWarning: password longer than %d characters specified in cifs password file",
480 if(filename
!= NULL
) {
481 close(file_descript
);
487 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
490 char * percent_char
= NULL
;
492 char * next_keyword
= NULL
;
500 if (!optionsp
|| !*optionsp
)
505 fprintf(stderr
, "parsing options: %s\n", data
);
507 /* BB fixme check for separator override BB */
511 snprintf(user
,sizeof(user
),"%u",getuid());
513 snprintf(group
,sizeof(group
),"%u",getgid());
516 /* while ((data = strsep(&options, ",")) != NULL) { */
517 while(data
!= NULL
) {
518 /* check if ends with trailing comma */
522 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
523 /* data = next keyword */
524 /* value = next value ie stuff after equal sign */
526 next_keyword
= strchr(data
,','); /* BB handle sep= */
528 /* temporarily null terminate end of keyword=value pair */
532 /* temporarily null terminate keyword to make keyword and value distinct */
533 if ((value
= strchr(data
, '=')) != NULL
) {
538 if (strncmp(data
, "users",5) == 0) {
539 if(!value
|| !*value
) {
540 *filesys_flags
|= MS_USERS
;
543 } else if (strncmp(data
, "user_xattr",10) == 0) {
544 /* do nothing - need to skip so not parsed as user name */
545 } else if (strncmp(data
, "user", 4) == 0) {
547 if (!value
|| !*value
) {
548 if(data
[4] == '\0') {
549 *filesys_flags
|= MS_USER
;
552 fprintf(stderr
, "username specified with no parameter\n");
554 return 1; /* needs_arg; */
557 if (strnlen(value
, 260) < 260) {
559 percent_char
= strchr(value
,'%');
562 if(mountpassword
== NULL
)
563 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
566 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
569 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
570 /* remove password from username */
571 while(*percent_char
!= 0) {
577 /* this is only case in which the user
578 name buf is not malloc - so we have to
579 check for domain name embedded within
580 the user name here since the later
581 call to check_for_domain will not be
583 domain_name
= check_for_domain(&value
);
585 fprintf(stderr
, "username too long\n");
590 } else if (strncmp(data
, "pass", 4) == 0) {
591 if (!value
|| !*value
) {
593 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
596 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
598 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
601 fprintf(stderr
, "password too long\n");
605 } else if (strncmp(data
, "sec", 3) == 0) {
607 if (!strncmp(value
, "none", 4) ||
608 !strncmp(value
, "krb5", 4))
611 } else if (strncmp(data
, "ip", 2) == 0) {
612 if (!value
|| !*value
) {
613 fprintf(stderr
, "target ip address argument missing");
614 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
616 fprintf(stderr
, "ip address %s override specified\n",value
);
619 fprintf(stderr
, "ip address too long\n");
623 } else if ((strncmp(data
, "unc", 3) == 0)
624 || (strncmp(data
, "target", 6) == 0)
625 || (strncmp(data
, "path", 4) == 0)) {
626 if (!value
|| !*value
) {
627 fprintf(stderr
, "invalid path to network resource\n");
629 return 1; /* needs_arg; */
630 } else if(strnlen(value
,5) < 5) {
631 fprintf(stderr
, "UNC name too short");
634 if (strnlen(value
, 300) < 300) {
636 if (strncmp(value
, "//", 2) == 0) {
638 fprintf(stderr
, "unc name specified twice, ignoring second\n");
641 } else if (strncmp(value
, "\\\\", 2) != 0) {
642 fprintf(stderr
, "UNC Path does not begin with // or \\\\ \n");
647 fprintf(stderr
, "unc name specified twice, ignoring second\n");
652 fprintf(stderr
, "CIFS: UNC name too long\n");
656 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
657 || (strncmp(data
, "workg", 5) == 0)) {
658 /* note this allows for synonyms of "domain"
659 such as "DOM" and "dom" and "workgroup"
660 and "WORKGRP" etc. */
661 if (!value
|| !*value
) {
662 fprintf(stderr
, "CIFS: invalid domain name\n");
664 return 1; /* needs_arg; */
666 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
669 fprintf(stderr
, "domain name too long\n");
673 } else if (strncmp(data
, "cred", 4) == 0) {
674 if (value
&& *value
) {
675 rc
= open_cred_file(value
);
677 fprintf(stderr
, "error %d (%s) opening credential file %s\n",
678 rc
, strerror(rc
), value
);
683 fprintf(stderr
, "invalid credential file name specified\n");
687 } else if (strncmp(data
, "uid", 3) == 0) {
688 if (value
&& *value
) {
690 if (!isdigit(*value
)) {
693 if (!(pw
= getpwnam(value
))) {
694 fprintf(stderr
, "bad user name \"%s\"\n", value
);
697 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
699 strlcpy(user
,value
,sizeof(user
));
703 } else if (strncmp(data
, "gid", 3) == 0) {
704 if (value
&& *value
) {
706 if (!isdigit(*value
)) {
709 if (!(gr
= getgrnam(value
))) {
710 fprintf(stderr
, "bad group name \"%s\"\n", value
);
713 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
715 strlcpy(group
,value
,sizeof(group
));
719 /* fmask and dmask synonyms for people used to smbfs syntax */
720 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
721 if (!value
|| !*value
) {
722 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
727 if (value
[0] != '0') {
728 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
731 if (strcmp (data
, "fmask") == 0) {
732 fprintf(stderr
, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
733 data
= "file_mode"; /* BB fix this */
735 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
736 if (!value
|| !*value
) {
737 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
742 if (value
[0] != '0') {
743 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
746 if (strcmp (data
, "dmask") == 0) {
747 fprintf(stderr
, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
750 /* the following eight mount options should be
751 stripped out from what is passed into the kernel
752 since these eight options are best passed as the
753 mount flags rather than redundantly to the kernel
754 and could generate spurious warnings depending on the
755 level of the corresponding cifs vfs kernel code */
756 } else if (strncmp(data
, "nosuid", 6) == 0) {
757 *filesys_flags
|= MS_NOSUID
;
758 } else if (strncmp(data
, "suid", 4) == 0) {
759 *filesys_flags
&= ~MS_NOSUID
;
760 } else if (strncmp(data
, "nodev", 5) == 0) {
761 *filesys_flags
|= MS_NODEV
;
762 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
763 (strncmp(data
, "nolock", 6) == 0)) {
764 *filesys_flags
&= ~MS_MANDLOCK
;
765 } else if (strncmp(data
, "dev", 3) == 0) {
766 *filesys_flags
&= ~MS_NODEV
;
767 } else if (strncmp(data
, "noexec", 6) == 0) {
768 *filesys_flags
|= MS_NOEXEC
;
769 } else if (strncmp(data
, "exec", 4) == 0) {
770 *filesys_flags
&= ~MS_NOEXEC
;
771 } else if (strncmp(data
, "guest", 5) == 0) {
772 user_name
= (char *)calloc(1, 1);
775 } else if (strncmp(data
, "ro", 2) == 0) {
776 *filesys_flags
|= MS_RDONLY
;
778 } else if (strncmp(data
, "rw", 2) == 0) {
779 *filesys_flags
&= ~MS_RDONLY
;
781 } else if (strncmp(data
, "remount", 7) == 0) {
782 *filesys_flags
|= MS_REMOUNT
;
783 } /* else if (strnicmp(data, "port", 4) == 0) {
784 if (value && *value) {
786 simple_strtoul(value, &value, 0);
788 } else if (strnicmp(data, "rsize", 5) == 0) {
789 if (value && *value) {
791 simple_strtoul(value, &value, 0);
793 } else if (strnicmp(data, "wsize", 5) == 0) {
794 if (value && *value) {
796 simple_strtoul(value, &value, 0);
798 } else if (strnicmp(data, "version", 3) == 0) {
800 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
801 } */ /* nothing to do on those four mount options above.
802 Just pass to kernel and ignore them here */
804 /* Copy (possibly modified) option to out */
805 word_len
= strlen(data
);
807 word_len
+= 1 + strlen(value
);
809 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
816 strlcat(out
, ",", out_len
+ word_len
+ 2);
821 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
823 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
824 out_len
= strlen(out
);
830 /* special-case the uid and gid */
832 word_len
= strlen(user
);
834 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
841 strlcat(out
, ",", out_len
+ word_len
+ 6);
844 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
845 out_len
= strlen(out
);
848 word_len
= strlen(group
);
850 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
857 strlcat(out
, ",", out_len
+ word_len
+ 6);
860 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
861 out_len
= strlen(out
);
864 SAFE_FREE(*optionsp
);
869 /* replace all (one or more) commas with double commas */
870 static void check_for_comma(char ** ppasswrd
)
875 int number_of_commas
= 0;
890 if(number_of_commas
== 0)
892 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
893 /* would otherwise overflow the mount options buffer */
894 fprintf(stderr
, "\nInvalid password. Password contains too many commas.\n");
898 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
899 if(new_pass_buf
== NULL
)
902 for(i
=0,j
=0;i
<len
;i
++,j
++) {
903 new_pass_buf
[j
] = pass
[i
];
906 new_pass_buf
[j
] = pass
[i
];
909 new_pass_buf
[len
+number_of_commas
] = 0;
911 SAFE_FREE(*ppasswrd
);
912 *ppasswrd
= new_pass_buf
;
917 /* Usernames can not have backslash in them and we use
918 [BB check if usernames can have forward slash in them BB]
919 backslash as domain\user separator character
921 static char * check_for_domain(char **ppuser
)
923 char * original_string
;
933 original_string
= *ppuser
;
935 if (original_string
== NULL
)
938 original_len
= strlen(original_string
);
940 usernm
= strchr(*ppuser
,'/');
941 if (usernm
== NULL
) {
942 usernm
= strchr(*ppuser
,'\\');
948 fprintf(stderr
, "Domain name specified twice. Username probably malformed\n");
954 if (domainnm
[0] != 0) {
957 fprintf(stderr
, "null domain\n");
959 len
= strlen(domainnm
);
960 /* reset domainm to new buffer, and copy
961 domain name into it */
962 domainnm
= (char *)malloc(len
+1);
966 strlcpy(domainnm
,*ppuser
,len
+1);
968 /* move_string(*ppuser, usernm+1) */
969 len
= strlen(usernm
+1);
971 if(len
>= original_len
) {
972 /* should not happen */
976 for(i
=0;i
<original_len
;i
++) {
978 original_string
[i
] = usernm
[i
+1];
979 else /* stuff with commas to remove last parm */
980 original_string
[i
] = ',';
983 /* BB add check for more than one slash?
991 /* replace all occurances of "from" in a string with "to" */
992 static void replace_char(char *string
, char from
, char to
, int maxlen
)
994 char *lastchar
= string
+ maxlen
;
996 string
= strchr(string
, from
);
999 if (string
>= lastchar
)
1005 /* Note that caller frees the returned buffer if necessary */
1006 static struct addrinfo
*
1007 parse_server(char ** punc_name
)
1009 char * unc_name
= *punc_name
;
1010 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1012 struct addrinfo
*addrlist
;
1015 if(length
> (MAX_UNC_LEN
- 1)) {
1016 fprintf(stderr
, "mount error: UNC name too long");
1019 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1020 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1021 fprintf(stderr
, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1026 /* BB add code to find DFS root here */
1027 fprintf(stderr
, "\nMounting the DFS root for domain not implemented yet\n");
1030 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1031 /* check for nfs syntax ie server:share */
1032 share
= strchr(unc_name
,':');
1034 *punc_name
= (char *)malloc(length
+3);
1035 if(*punc_name
== NULL
) {
1036 /* put the original string back if
1038 *punc_name
= unc_name
;
1042 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1043 SAFE_FREE(unc_name
);
1044 unc_name
= *punc_name
;
1045 unc_name
[length
+2] = 0;
1046 goto continue_unc_parsing
;
1048 fprintf(stderr
, "mount error: improperly formatted UNC name.");
1049 fprintf(stderr
, " %s does not begin with \\\\ or //\n",unc_name
);
1053 continue_unc_parsing
:
1058 /* allow for either delimiter between host and sharename */
1059 if ((share
= strpbrk(unc_name
, "/\\"))) {
1060 *share
= 0; /* temporarily terminate the string */
1063 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1065 fprintf(stderr
, "mount error: could not resolve address for %s: %s\n",
1066 unc_name
, gai_strerror(rc
));
1070 *(share
- 1) = '/'; /* put delimiter back */
1072 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1073 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1074 *prefixpath
= 0; /* permanently terminate the string */
1075 if (!strlen(++prefixpath
))
1076 prefixpath
= NULL
; /* this needs to be done explicitly */
1080 fprintf(stderr
, "ip address specified explicitly\n");
1083 /* BB should we pass an alternate version of the share name as Unicode */
1087 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1088 fprintf(stderr
, "Mounting the DFS root for a particular server not implemented yet\n");
1095 static struct option longopts
[] = {
1096 { "all", 0, NULL
, 'a' },
1097 { "help",0, NULL
, 'h' },
1098 { "move",0, NULL
, 'm' },
1099 { "bind",0, NULL
, 'b' },
1100 { "read-only", 0, NULL
, 'r' },
1101 { "ro", 0, NULL
, 'r' },
1102 { "verbose", 0, NULL
, 'v' },
1103 { "version", 0, NULL
, 'V' },
1104 { "read-write", 0, NULL
, 'w' },
1105 { "rw", 0, NULL
, 'w' },
1106 { "options", 1, NULL
, 'o' },
1107 { "type", 1, NULL
, 't' },
1108 { "rsize",1, NULL
, 'R' },
1109 { "wsize",1, NULL
, 'W' },
1110 { "uid", 1, NULL
, '1'},
1111 { "gid", 1, NULL
, '2'},
1112 { "user",1,NULL
,'u'},
1113 { "username",1,NULL
,'u'},
1114 { "dom",1,NULL
,'d'},
1115 { "domain",1,NULL
,'d'},
1116 { "password",1,NULL
,'p'},
1117 { "pass",1,NULL
,'p'},
1118 { "credentials",1,NULL
,'c'},
1119 { "port",1,NULL
,'P'},
1120 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1121 { NULL
, 0, NULL
, 0 }
1124 /* convert a string to uppercase. return false if the string
1125 * wasn't ASCII. Return success on a NULL ptr */
1127 uppercase_string(char *string
)
1133 /* check for unicode */
1134 if ((unsigned char) string
[0] & 0x80)
1136 *string
= toupper((unsigned char) *string
);
1143 static void print_cifs_mount_version(void)
1145 printf("mount.cifs version: %s.%s%s\n",
1146 MOUNT_CIFS_VERSION_MAJOR
,
1147 MOUNT_CIFS_VERSION_MINOR
,
1148 MOUNT_CIFS_VENDOR_SUFFIX
);
1151 int main(int argc
, char ** argv
)
1154 unsigned long flags
= MS_MANDLOCK
;
1155 char * orgoptions
= NULL
;
1156 char * share_name
= NULL
;
1157 const char * ipaddr
= NULL
;
1159 char * mountpoint
= NULL
;
1160 char * options
= NULL
;
1162 char * resolved_path
= NULL
;
1173 size_t options_size
= 0;
1175 int retry
= 0; /* set when we have to retry mount with uppercase */
1176 struct addrinfo
*addrhead
= NULL
, *addr
;
1177 struct utsname sysinfo
;
1178 struct mntent mountent
;
1179 struct sockaddr_in
*addr4
;
1180 struct sockaddr_in6
*addr6
;
1183 /* setlocale(LC_ALL, "");
1184 bindtextdomain(PACKAGE, LOCALEDIR);
1185 textdomain(PACKAGE); */
1188 thisprogram
= argv
[0];
1190 mount_cifs_usage(stderr
);
1192 if(thisprogram
== NULL
)
1193 thisprogram
= "mount.cifs";
1196 /* BB add workstation name and domain and pass down */
1198 /* #ifdef _GNU_SOURCE
1199 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1203 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1204 if (share_name
== NULL
) {
1205 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1208 mountpoint
= argv
[2];
1209 } else if (argc
== 2) {
1210 if ((strcmp(argv
[1], "-V") == 0) ||
1211 (strcmp(argv
[1], "--version") == 0))
1213 print_cifs_mount_version();
1217 if ((strcmp(argv
[1], "-h") == 0) ||
1218 (strcmp(argv
[1], "-?") == 0) ||
1219 (strcmp(argv
[1], "--help") == 0))
1220 mount_cifs_usage(stdout
);
1222 mount_cifs_usage(stderr
);
1224 mount_cifs_usage(stderr
);
1228 /* add sharename in opts string as unc= parm */
1229 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1230 longopts
, NULL
)) != -1) {
1232 /* No code to do the following options yet */
1234 list_with_volumelabel = 1;
1237 volumelabel = optarg;
1244 case 'h': /* help */
1245 mount_cifs_usage(stdout
);
1254 "option 'b' (MS_BIND) not supported\n");
1262 "option 'm' (MS_MOVE) not supported\n");
1266 orgoptions
= strdup(optarg
);
1268 case 'r': /* mount readonly */
1278 print_cifs_mount_version();
1281 flags
&= ~MS_RDONLY
;
1284 rsize
= atoi(optarg
) ;
1287 wsize
= atoi(optarg
);
1290 if (isdigit(*optarg
)) {
1293 uid
= strtoul(optarg
, &ep
, 10);
1295 fprintf(stderr
, "bad uid value \"%s\"\n", optarg
);
1301 if (!(pw
= getpwnam(optarg
))) {
1302 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1310 if (isdigit(*optarg
)) {
1313 gid
= strtoul(optarg
, &ep
, 10);
1315 fprintf(stderr
, "bad gid value \"%s\"\n", optarg
);
1321 if (!(gr
= getgrnam(optarg
))) {
1322 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1334 domain_name
= optarg
; /* BB fix this - currently ignored */
1338 if(mountpassword
== NULL
)
1339 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1342 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1346 get_password_from_file(0 /* stdin */,NULL
);
1354 fprintf(stderr
, "unknown mount option %c\n",c
);
1355 mount_cifs_usage(stderr
);
1359 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1360 mount_cifs_usage(stderr
);
1363 /* make sure mountpoint is legit */
1364 rc
= check_mountpoint(thisprogram
, mountpoint
);
1368 /* sanity check for unprivileged mounts */
1370 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1375 /* enable any default user mount flags */
1376 flags
|= CIFS_SETUID_FLAGS
;
1379 if (getenv("PASSWD")) {
1380 if(mountpassword
== NULL
)
1381 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1383 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1386 } else if (getenv("PASSWD_FD")) {
1387 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1388 } else if (getenv("PASSWD_FILE")) {
1389 get_password_from_file(0, getenv("PASSWD_FILE"));
1392 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1398 #if !CIFS_LEGACY_SETUID_CHECK
1399 if (!(flags
& (MS_USERS
|MS_USER
))) {
1400 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1404 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1407 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1408 "CIFS mounts not supported.",
1415 flags
&= ~(MS_USERS
|MS_USER
);
1417 addrhead
= addr
= parse_server(&share_name
);
1418 if((addrhead
== NULL
) && (got_ip
== 0)) {
1419 fprintf(stderr
, "No ip address specified and hostname not found\n");
1424 /* BB save off path and pop after mount returns? */
1425 resolved_path
= (char *)malloc(PATH_MAX
+1);
1427 /* Note that if we can not canonicalize the name, we get
1428 another chance to see if it is valid when we chdir to it */
1429 if (realpath(mountpoint
, resolved_path
)) {
1430 mountpoint
= resolved_path
;
1434 /* Note that the password will not be retrieved from the
1435 USER env variable (ie user%password form) as there is
1436 already a PASSWD environment varaible */
1438 user_name
= strdup(getenv("USER"));
1439 if (user_name
== NULL
)
1440 user_name
= getusername();
1444 if(got_password
== 0) {
1445 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1446 no good replacement yet. */
1447 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1448 if (!tmp_pass
|| !mountpassword
) {
1449 fprintf(stderr
, "Password not entered, exiting\n");
1452 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1455 /* FIXME launch daemon (handles dfs name resolution and credential change)
1456 remember to clear parms and overwrite password field before launching */
1458 optlen
= strlen(orgoptions
);
1463 optlen
+= strlen(share_name
) + 4;
1465 fprintf(stderr
, "No server share name specified\n");
1466 fprintf(stderr
, "\nMounting the DFS root for server not implemented yet\n");
1470 optlen
+= strlen(user_name
) + 6;
1471 optlen
+= MAX_ADDRESS_LEN
+ 4;
1473 optlen
+= strlen(mountpassword
) + 6;
1476 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1477 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 */);
1479 if(options
== NULL
) {
1480 fprintf(stderr
, "Could not allocate memory for mount options\n");
1484 strlcpy(options
, "unc=", options_size
);
1485 strlcat(options
,share_name
,options_size
);
1486 /* scan backwards and reverse direction of slash */
1487 temp
= strrchr(options
, '/');
1488 if(temp
> options
+ 6)
1491 /* check for syntax like user=domain\user */
1493 domain_name
= check_for_domain(&user_name
);
1494 strlcat(options
,",user=",options_size
);
1495 strlcat(options
,user_name
,options_size
);
1499 /* extra length accounted for in option string above */
1500 strlcat(options
,",domain=",options_size
);
1501 strlcat(options
,domain_name
,options_size
);
1505 /* Commas have to be doubled, or else they will
1506 look like the parameter separator */
1507 /* if(sep is not set)*/
1509 check_for_comma(&mountpassword
);
1510 strlcat(options
,",pass=",options_size
);
1511 strlcat(options
,mountpassword
,options_size
);
1514 strlcat(options
,",ver=",options_size
);
1515 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1518 strlcat(options
,",",options_size
);
1519 strlcat(options
,orgoptions
,options_size
);
1522 strlcat(options
,",prefixpath=",options_size
);
1523 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1526 fprintf(stderr
, "\nmount.cifs kernel mount options %s \n",options
);
1528 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1529 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1531 if (!got_ip
&& addr
) {
1532 strlcat(options
, ",ip=", options_size
);
1533 current_len
= strnlen(options
, options_size
);
1534 optionstail
= options
+ current_len
;
1535 switch (addr
->ai_addr
->sa_family
) {
1537 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1538 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1539 options_size
- current_len
);
1542 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1543 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1544 options_size
- current_len
);
1550 /* if the address looks bogus, try the next one */
1552 addr
= addr
->ai_next
;
1560 if (addr
->ai_addr
->sa_family
== AF_INET6
&& addr6
->sin6_scope_id
) {
1561 strlcat(options
, "%", options_size
);
1562 current_len
= strnlen(options
, options_size
);
1563 optionstail
= options
+ current_len
;
1564 snprintf(optionstail
, options_size
- current_len
, "%u",
1565 addr6
->sin6_scope_id
);
1568 if (!fakemnt
&& mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1573 addr
= addr
->ai_next
;
1579 fprintf(stderr
, "mount error: cifs filesystem not supported by the system\n");
1584 if (uppercase_string(dev_name
) &&
1585 uppercase_string(share_name
) &&
1586 uppercase_string(prefixpath
)) {
1587 fprintf(stderr
, "retrying with upper case share name\n");
1592 fprintf(stderr
, "mount error(%d): %s\n", errno
, strerror(errno
));
1593 fprintf(stderr
, "Refer to the mount.cifs(8) manual page (e.g. man "
1601 atexit(unlock_mtab
);
1604 fprintf(stderr
, "cannot lock mtab");
1607 pmntfile
= setmntent(MOUNTED
, "a+");
1609 fprintf(stderr
, "could not update mount table\n");
1614 mountent
.mnt_fsname
= dev_name
;
1615 mountent
.mnt_dir
= mountpoint
;
1616 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1617 mountent
.mnt_opts
= (char *)malloc(220);
1618 if(mountent
.mnt_opts
) {
1619 char * mount_user
= getusername();
1620 memset(mountent
.mnt_opts
,0,200);
1621 if(flags
& MS_RDONLY
)
1622 strlcat(mountent
.mnt_opts
,"ro",220);
1624 strlcat(mountent
.mnt_opts
,"rw",220);
1625 if(flags
& MS_MANDLOCK
)
1626 strlcat(mountent
.mnt_opts
,",mand",220);
1627 if(flags
& MS_NOEXEC
)
1628 strlcat(mountent
.mnt_opts
,",noexec",220);
1629 if(flags
& MS_NOSUID
)
1630 strlcat(mountent
.mnt_opts
,",nosuid",220);
1631 if(flags
& MS_NODEV
)
1632 strlcat(mountent
.mnt_opts
,",nodev",220);
1633 if(flags
& MS_SYNCHRONOUS
)
1634 strlcat(mountent
.mnt_opts
,",sync",220);
1637 strlcat(mountent
.mnt_opts
,
1639 strlcat(mountent
.mnt_opts
,
1644 mountent
.mnt_freq
= 0;
1645 mountent
.mnt_passno
= 0;
1646 rc
= addmntent(pmntfile
,&mountent
);
1647 endmntent(pmntfile
);
1649 SAFE_FREE(mountent
.mnt_opts
);
1654 int len
= strlen(mountpassword
);
1655 memset(mountpassword
,0,len
);
1656 SAFE_FREE(mountpassword
);
1660 freeaddrinfo(addrhead
);
1662 SAFE_FREE(orgoptions
);
1663 SAFE_FREE(resolved_path
);
1664 SAFE_FREE(share_name
);