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
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 * By default, mount.cifs follows the conventions set forth by /bin/mount
91 * for user mounts. That is, it requires that the mount be listed in
92 * /etc/fstab with the "user" option when run as an unprivileged user and
93 * mount.cifs is setuid root.
95 * Older versions of mount.cifs however were "looser" in this regard. When
96 * made setuid root, a user could run mount.cifs directly and mount any share
97 * on a directory owned by that user.
99 * The legacy behavior is now disabled by default. To reenable it, set the
100 * following #define to true.
102 #define CIFS_LEGACY_SETUID_CHECK 0
105 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
106 * flags by default. These defaults can be changed here.
108 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
110 const char *thisprogram
;
113 static int got_password
= 0;
114 static int got_user
= 0;
115 static int got_domain
= 0;
116 static int got_ip
= 0;
117 static int got_unc
= 0;
118 static int got_uid
= 0;
119 static int got_gid
= 0;
120 static char * user_name
= NULL
;
121 static char * mountpassword
= NULL
;
122 char * domain_name
= NULL
;
123 char * prefixpath
= NULL
;
124 const char *cifs_fstype
= "cifs";
126 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
127 * don't link to libreplace so need them here. */
129 /* like strncpy but does not 0 fill the buffer and always null
130 * terminates. bufsize is the size of the destination buffer */
133 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
135 size_t len
= strlen(s
);
137 if (bufsize
<= 0) return 0;
138 if (len
>= bufsize
) len
= bufsize
-1;
145 /* like strncat but does not 0 fill the buffer and always null
146 * terminates. bufsize is the length of the buffer, which should
147 * be one more than the maximum resulting string length */
150 static size_t strlcat(char *d
, const char *s
, size_t bufsize
)
152 size_t len1
= strlen(d
);
153 size_t len2
= strlen(s
);
154 size_t ret
= len1
+ len2
;
156 if (len1
+len2
>= bufsize
) {
157 if (bufsize
< (len1
+1)) {
160 len2
= bufsize
- (len1
+1);
163 memcpy(d
+len1
, s
, len2
);
171 * If an unprivileged user is doing the mounting then we need to ensure
172 * that the entry is in /etc/fstab.
175 check_mountpoint(const char *progname
, char *mountpoint
)
180 /* does mountpoint exist and is it a directory? */
181 err
= stat(mountpoint
, &statbuf
);
183 fprintf(stderr
, "%s: failed to stat %s: %s\n", progname
,
184 mountpoint
, strerror(errno
));
188 if (!S_ISDIR(statbuf
.st_mode
)) {
189 fprintf(stderr
, "%s: %s is not a directory!", progname
,
194 #if CIFS_LEGACY_SETUID_CHECK
195 /* do extra checks on mountpoint for legacy setuid behavior */
196 if (!getuid() || geteuid())
199 if (statbuf
.st_uid
!= getuid()) {
200 fprintf(stderr
, "%s: %s is not owned by user\n", progname
,
205 if ((statbuf
.st_mode
& S_IRWXU
) != S_IRWXU
) {
206 fprintf(stderr
, "%s: invalid permissions on %s\n", progname
,
210 #endif /* CIFS_LEGACY_SETUID_CHECK */
215 #if CIFS_LEGACY_SETUID_CHECK
217 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
222 #else /* CIFS_LEGACY_SETUID_CHECK */
224 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
230 /* make sure this mount is listed in /etc/fstab */
231 fstab
= setmntent(_PATH_FSTAB
, "r");
233 fprintf(stderr
, "Couldn't open %s for reading!\n",
238 while((mnt
= getmntent(fstab
))) {
239 if (!strcmp(mountpoint
, mnt
->mnt_dir
))
244 if (mnt
== NULL
|| strcmp(mnt
->mnt_fsname
, devname
)) {
245 fprintf(stderr
, "%s: permission denied: no match for "
246 "%s found in %s\n", progname
, mountpoint
,
252 * 'mount' munges the options from fstab before passing them
253 * to us. It is non-trivial to test that we have the correct
254 * set of options. We don't want to trust what the user
255 * gave us, so just take whatever is in /etc/fstab.
258 *options
= strdup(mnt
->mnt_opts
);
261 #endif /* CIFS_LEGACY_SETUID_CHECK */
266 open nofollow - avoid symlink exposure?
267 get owner of dir see if matches self or if root
268 call system(umount argv) etc.
272 static char * check_for_domain(char **);
275 static void mount_cifs_usage(FILE *stream
)
277 fprintf(stream
, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
278 fprintf(stream
, "\nMount the remote target, specified as a UNC name,");
279 fprintf(stream
, " to a local directory.\n\nOptions:\n");
280 fprintf(stream
, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
281 fprintf(stream
, "\nLess commonly used options:");
282 fprintf(stream
, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
283 fprintf(stream
, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
284 fprintf(stream
, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
285 fprintf(stream
, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
286 fprintf(stream
, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
287 fprintf(stream
, "\n\t(e.g. unneeded for mounts to most Samba versions):");
288 fprintf(stream
, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
289 fprintf(stream
, "\n\nRarely used options:");
290 fprintf(stream
, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
291 fprintf(stream
, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
292 fprintf(stream
, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
293 fprintf(stream
, "\n\nOptions are described in more detail in the manual page");
294 fprintf(stream
, "\n\tman 8 mount.cifs\n");
295 fprintf(stream
, "\nTo display the version number of the mount helper:");
296 fprintf(stream
, "\n\t%s -V\n",thisprogram
);
298 SAFE_FREE(mountpassword
);
300 if (stream
== stderr
)
305 /* caller frees username if necessary */
306 static char * getusername(void) {
307 char *username
= NULL
;
308 struct passwd
*password
= getpwuid(getuid());
311 username
= password
->pw_name
;
316 static int open_cred_file(char * file_name
)
323 i
= access(file_name
, R_OK
);
327 fs
= fopen(file_name
,"r");
330 line_buf
= (char *)malloc(4096);
331 if(line_buf
== NULL
) {
336 while(fgets(line_buf
,4096,fs
)) {
337 /* parse line from credential file */
339 /* eat leading white space */
340 for(i
=0;i
<4086;i
++) {
341 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
343 /* if whitespace - skip past it */
345 if (strncasecmp("username",line_buf
+i
,8) == 0) {
346 temp_val
= strchr(line_buf
+ i
,'=');
348 /* go past equals sign */
350 for(length
= 0;length
<4087;length
++) {
351 if ((temp_val
[length
] == '\n')
352 || (temp_val
[length
] == '\0')) {
353 temp_val
[length
] = '\0';
358 fprintf(stderr
, "mount.cifs failed due to malformed username in credentials file\n");
359 memset(line_buf
,0,4096);
363 user_name
= (char *)calloc(1 + length
,1);
364 /* BB adding free of user_name string before exit,
365 not really necessary but would be cleaner */
366 strlcpy(user_name
,temp_val
, length
+1);
369 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
370 temp_val
= strchr(line_buf
+i
,'=');
372 /* go past equals sign */
374 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
375 if ((temp_val
[length
] == '\n')
376 || (temp_val
[length
] == '\0')) {
377 temp_val
[length
] = '\0';
381 if(length
> MOUNT_PASSWD_SIZE
) {
382 fprintf(stderr
, "mount.cifs failed: password in credentials file too long\n");
383 memset(line_buf
,0, 4096);
386 if(mountpassword
== NULL
) {
387 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
389 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
391 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
396 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
397 temp_val
= strchr(line_buf
+i
,'=');
399 /* go past equals sign */
402 fprintf(stderr
, "\nDomain %s\n",temp_val
);
403 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
404 if ((temp_val
[length
] == '\n')
405 || (temp_val
[length
] == '\0')) {
406 temp_val
[length
] = '\0';
410 if(length
> DOMAIN_SIZE
) {
411 fprintf(stderr
, "mount.cifs failed: domain in credentials file too long\n");
414 if(domain_name
== NULL
) {
415 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
417 memset(domain_name
,0,DOMAIN_SIZE
);
419 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
432 static int get_password_from_file(int file_descript
, char * filename
)
438 if(mountpassword
== NULL
)
439 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
441 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
443 if (mountpassword
== NULL
) {
444 fprintf(stderr
, "malloc failed\n");
448 if(filename
!= NULL
) {
449 rc
= access(filename
, R_OK
);
451 fprintf(stderr
, "mount.cifs failed: access check of %s failed: %s\n",
452 filename
, strerror(errno
));
455 file_descript
= open(filename
, O_RDONLY
);
456 if(file_descript
< 0) {
457 fprintf(stderr
, "mount.cifs failed. %s attempting to open password file %s\n",
458 strerror(errno
),filename
);
462 /* else file already open and fd provided */
464 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
465 rc
= read(file_descript
,&c
,1);
467 fprintf(stderr
, "mount.cifs failed. Error %s reading password file\n",strerror(errno
));
469 close(file_descript
);
472 if(mountpassword
[0] == 0) {
474 fprintf(stderr
, "\nWarning: null password used since cifs password file empty");
477 } else /* read valid character */ {
478 if((c
== 0) || (c
== '\n')) {
479 mountpassword
[i
] = '\0';
482 mountpassword
[i
] = c
;
485 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
486 fprintf(stderr
, "\nWarning: password longer than %d characters specified in cifs password file",
490 if(filename
!= NULL
) {
491 close(file_descript
);
497 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
500 char * percent_char
= NULL
;
502 char * next_keyword
= NULL
;
510 if (!optionsp
|| !*optionsp
)
514 /* BB fixme check for separator override BB */
518 snprintf(user
,sizeof(user
),"%u",getuid());
520 snprintf(group
,sizeof(group
),"%u",getgid());
523 /* while ((data = strsep(&options, ",")) != NULL) { */
524 while(data
!= NULL
) {
525 /* check if ends with trailing comma */
529 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
530 /* data = next keyword */
531 /* value = next value ie stuff after equal sign */
533 next_keyword
= strchr(data
,','); /* BB handle sep= */
535 /* temporarily null terminate end of keyword=value pair */
539 /* temporarily null terminate keyword to make keyword and value distinct */
540 if ((value
= strchr(data
, '=')) != NULL
) {
545 if (strncmp(data
, "users",5) == 0) {
546 if(!value
|| !*value
) {
547 *filesys_flags
|= MS_USERS
;
550 } else if (strncmp(data
, "user_xattr",10) == 0) {
551 /* do nothing - need to skip so not parsed as user name */
552 } else if (strncmp(data
, "user", 4) == 0) {
554 if (!value
|| !*value
) {
555 if(data
[4] == '\0') {
556 *filesys_flags
|= MS_USER
;
559 fprintf(stderr
, "username specified with no parameter\n");
561 return 1; /* needs_arg; */
564 if (strnlen(value
, 260) < 260) {
566 percent_char
= strchr(value
,'%');
569 if(mountpassword
== NULL
)
570 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
573 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
576 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
577 /* remove password from username */
578 while(*percent_char
!= 0) {
584 /* this is only case in which the user
585 name buf is not malloc - so we have to
586 check for domain name embedded within
587 the user name here since the later
588 call to check_for_domain will not be
590 domain_name
= check_for_domain(&value
);
592 fprintf(stderr
, "username too long\n");
597 } else if (strncmp(data
, "pass", 4) == 0) {
598 if (!value
|| !*value
) {
600 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
603 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
605 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
607 mountpassword
= strndup(value
, MOUNT_PASSWD_SIZE
);
608 if (!mountpassword
) {
609 fprintf(stderr
, "mount.cifs error: %s", strerror(ENOMEM
));
616 fprintf(stderr
, "password too long\n");
621 } else if (strncmp(data
, "sec", 3) == 0) {
623 if (!strncmp(value
, "none", 4) ||
624 !strncmp(value
, "krb5", 4))
627 } else if (strncmp(data
, "ip", 2) == 0) {
628 if (!value
|| !*value
) {
629 fprintf(stderr
, "target ip address argument missing");
630 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
632 fprintf(stderr
, "ip address %s override specified\n",value
);
635 fprintf(stderr
, "ip address too long\n");
639 } else if ((strncmp(data
, "unc", 3) == 0)
640 || (strncmp(data
, "target", 6) == 0)
641 || (strncmp(data
, "path", 4) == 0)) {
642 if (!value
|| !*value
) {
643 fprintf(stderr
, "invalid path to network resource\n");
645 return 1; /* needs_arg; */
646 } else if(strnlen(value
,5) < 5) {
647 fprintf(stderr
, "UNC name too short");
650 if (strnlen(value
, 300) < 300) {
652 if (strncmp(value
, "//", 2) == 0) {
654 fprintf(stderr
, "unc name specified twice, ignoring second\n");
657 } else if (strncmp(value
, "\\\\", 2) != 0) {
658 fprintf(stderr
, "UNC Path does not begin with // or \\\\ \n");
663 fprintf(stderr
, "unc name specified twice, ignoring second\n");
668 fprintf(stderr
, "CIFS: UNC name too long\n");
672 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
673 || (strncmp(data
, "workg", 5) == 0)) {
674 /* note this allows for synonyms of "domain"
675 such as "DOM" and "dom" and "workgroup"
676 and "WORKGRP" etc. */
677 if (!value
|| !*value
) {
678 fprintf(stderr
, "CIFS: invalid domain name\n");
680 return 1; /* needs_arg; */
682 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
685 fprintf(stderr
, "domain name too long\n");
689 } else if (strncmp(data
, "cred", 4) == 0) {
690 if (value
&& *value
) {
691 rc
= open_cred_file(value
);
693 fprintf(stderr
, "error %d (%s) opening credential file %s\n",
694 rc
, strerror(rc
), value
);
699 fprintf(stderr
, "invalid credential file name specified\n");
703 } else if (strncmp(data
, "uid", 3) == 0) {
704 if (value
&& *value
) {
706 if (!isdigit(*value
)) {
709 if (!(pw
= getpwnam(value
))) {
710 fprintf(stderr
, "bad user name \"%s\"\n", value
);
713 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
715 strlcpy(user
,value
,sizeof(user
));
719 } else if (strncmp(data
, "gid", 3) == 0) {
720 if (value
&& *value
) {
722 if (!isdigit(*value
)) {
725 if (!(gr
= getgrnam(value
))) {
726 fprintf(stderr
, "bad group name \"%s\"\n", value
);
729 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
731 strlcpy(group
,value
,sizeof(group
));
735 /* fmask and dmask synonyms for people used to smbfs syntax */
736 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
737 if (!value
|| !*value
) {
738 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
743 if (value
[0] != '0') {
744 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
747 if (strcmp (data
, "fmask") == 0) {
748 fprintf(stderr
, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
749 data
= "file_mode"; /* BB fix this */
751 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
752 if (!value
|| !*value
) {
753 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
758 if (value
[0] != '0') {
759 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
762 if (strcmp (data
, "dmask") == 0) {
763 fprintf(stderr
, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
766 /* the following eight mount options should be
767 stripped out from what is passed into the kernel
768 since these eight options are best passed as the
769 mount flags rather than redundantly to the kernel
770 and could generate spurious warnings depending on the
771 level of the corresponding cifs vfs kernel code */
772 } else if (strncmp(data
, "nosuid", 6) == 0) {
773 *filesys_flags
|= MS_NOSUID
;
774 } else if (strncmp(data
, "suid", 4) == 0) {
775 *filesys_flags
&= ~MS_NOSUID
;
776 } else if (strncmp(data
, "nodev", 5) == 0) {
777 *filesys_flags
|= MS_NODEV
;
778 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
779 (strncmp(data
, "nolock", 6) == 0)) {
780 *filesys_flags
&= ~MS_MANDLOCK
;
781 } else if (strncmp(data
, "dev", 3) == 0) {
782 *filesys_flags
&= ~MS_NODEV
;
783 } else if (strncmp(data
, "noexec", 6) == 0) {
784 *filesys_flags
|= MS_NOEXEC
;
785 } else if (strncmp(data
, "exec", 4) == 0) {
786 *filesys_flags
&= ~MS_NOEXEC
;
787 } else if (strncmp(data
, "guest", 5) == 0) {
788 user_name
= (char *)calloc(1, 1);
791 } else if (strncmp(data
, "ro", 2) == 0) {
792 *filesys_flags
|= MS_RDONLY
;
794 } else if (strncmp(data
, "rw", 2) == 0) {
795 *filesys_flags
&= ~MS_RDONLY
;
797 } else if (strncmp(data
, "remount", 7) == 0) {
798 *filesys_flags
|= MS_REMOUNT
;
799 } /* else if (strnicmp(data, "port", 4) == 0) {
800 if (value && *value) {
802 simple_strtoul(value, &value, 0);
804 } else if (strnicmp(data, "rsize", 5) == 0) {
805 if (value && *value) {
807 simple_strtoul(value, &value, 0);
809 } else if (strnicmp(data, "wsize", 5) == 0) {
810 if (value && *value) {
812 simple_strtoul(value, &value, 0);
814 } else if (strnicmp(data, "version", 3) == 0) {
816 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
817 } */ /* nothing to do on those four mount options above.
818 Just pass to kernel and ignore them here */
820 /* Copy (possibly modified) option to out */
821 word_len
= strlen(data
);
823 word_len
+= 1 + strlen(value
);
825 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
832 strlcat(out
, ",", out_len
+ word_len
+ 2);
837 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
839 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
840 out_len
= strlen(out
);
846 /* special-case the uid and gid */
848 word_len
= strlen(user
);
850 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
857 strlcat(out
, ",", out_len
+ word_len
+ 6);
860 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
861 out_len
= strlen(out
);
864 word_len
= strlen(group
);
866 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
873 strlcat(out
, ",", out_len
+ word_len
+ 6);
876 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
877 out_len
= strlen(out
);
880 SAFE_FREE(*optionsp
);
885 /* replace all (one or more) commas with double commas */
886 static void check_for_comma(char ** ppasswrd
)
891 int number_of_commas
= 0;
906 if(number_of_commas
== 0)
908 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
909 /* would otherwise overflow the mount options buffer */
910 fprintf(stderr
, "\nInvalid password. Password contains too many commas.\n");
914 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
915 if(new_pass_buf
== NULL
)
918 for(i
=0,j
=0;i
<len
;i
++,j
++) {
919 new_pass_buf
[j
] = pass
[i
];
922 new_pass_buf
[j
] = pass
[i
];
925 new_pass_buf
[len
+number_of_commas
] = 0;
927 SAFE_FREE(*ppasswrd
);
928 *ppasswrd
= new_pass_buf
;
933 /* Usernames can not have backslash in them and we use
934 [BB check if usernames can have forward slash in them BB]
935 backslash as domain\user separator character
937 static char * check_for_domain(char **ppuser
)
939 char * original_string
;
949 original_string
= *ppuser
;
951 if (original_string
== NULL
)
954 original_len
= strlen(original_string
);
956 usernm
= strchr(*ppuser
,'/');
957 if (usernm
== NULL
) {
958 usernm
= strchr(*ppuser
,'\\');
964 fprintf(stderr
, "Domain name specified twice. Username probably malformed\n");
970 if (domainnm
[0] != 0) {
973 fprintf(stderr
, "null domain\n");
975 len
= strlen(domainnm
);
976 /* reset domainm to new buffer, and copy
977 domain name into it */
978 domainnm
= (char *)malloc(len
+1);
982 strlcpy(domainnm
,*ppuser
,len
+1);
984 /* move_string(*ppuser, usernm+1) */
985 len
= strlen(usernm
+1);
987 if(len
>= original_len
) {
988 /* should not happen */
992 for(i
=0;i
<original_len
;i
++) {
994 original_string
[i
] = usernm
[i
+1];
995 else /* stuff with commas to remove last parm */
996 original_string
[i
] = ',';
999 /* BB add check for more than one slash?
1000 strchr(*ppuser,'/');
1001 strchr(*ppuser,'\\')
1007 /* replace all occurances of "from" in a string with "to" */
1008 static void replace_char(char *string
, char from
, char to
, int maxlen
)
1010 char *lastchar
= string
+ maxlen
;
1012 string
= strchr(string
, from
);
1015 if (string
>= lastchar
)
1021 /* Note that caller frees the returned buffer if necessary */
1022 static struct addrinfo
*
1023 parse_server(char ** punc_name
)
1025 char * unc_name
= *punc_name
;
1026 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1028 struct addrinfo
*addrlist
;
1031 if(length
> (MAX_UNC_LEN
- 1)) {
1032 fprintf(stderr
, "mount error: UNC name too long");
1035 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1036 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1037 fprintf(stderr
, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1042 /* BB add code to find DFS root here */
1043 fprintf(stderr
, "\nMounting the DFS root for domain not implemented yet\n");
1046 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1047 /* check for nfs syntax ie server:share */
1048 share
= strchr(unc_name
,':');
1050 *punc_name
= (char *)malloc(length
+3);
1051 if(*punc_name
== NULL
) {
1052 /* put the original string back if
1054 *punc_name
= unc_name
;
1058 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1059 SAFE_FREE(unc_name
);
1060 unc_name
= *punc_name
;
1061 unc_name
[length
+2] = 0;
1062 goto continue_unc_parsing
;
1064 fprintf(stderr
, "mount error: improperly formatted UNC name.");
1065 fprintf(stderr
, " %s does not begin with \\\\ or //\n",unc_name
);
1069 continue_unc_parsing
:
1074 /* allow for either delimiter between host and sharename */
1075 if ((share
= strpbrk(unc_name
, "/\\"))) {
1076 *share
= 0; /* temporarily terminate the string */
1079 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1081 fprintf(stderr
, "mount error: could not resolve address for %s: %s\n",
1082 unc_name
, gai_strerror(rc
));
1086 *(share
- 1) = '/'; /* put delimiter back */
1088 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1089 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1090 *prefixpath
= 0; /* permanently terminate the string */
1091 if (!strlen(++prefixpath
))
1092 prefixpath
= NULL
; /* this needs to be done explicitly */
1096 fprintf(stderr
, "ip address specified explicitly\n");
1099 /* BB should we pass an alternate version of the share name as Unicode */
1103 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1104 fprintf(stderr
, "Mounting the DFS root for a particular server not implemented yet\n");
1111 static struct option longopts
[] = {
1112 { "all", 0, NULL
, 'a' },
1113 { "help",0, NULL
, 'h' },
1114 { "move",0, NULL
, 'm' },
1115 { "bind",0, NULL
, 'b' },
1116 { "read-only", 0, NULL
, 'r' },
1117 { "ro", 0, NULL
, 'r' },
1118 { "verbose", 0, NULL
, 'v' },
1119 { "version", 0, NULL
, 'V' },
1120 { "read-write", 0, NULL
, 'w' },
1121 { "rw", 0, NULL
, 'w' },
1122 { "options", 1, NULL
, 'o' },
1123 { "type", 1, NULL
, 't' },
1124 { "rsize",1, NULL
, 'R' },
1125 { "wsize",1, NULL
, 'W' },
1126 { "uid", 1, NULL
, '1'},
1127 { "gid", 1, NULL
, '2'},
1128 { "user",1,NULL
,'u'},
1129 { "username",1,NULL
,'u'},
1130 { "dom",1,NULL
,'d'},
1131 { "domain",1,NULL
,'d'},
1132 { "password",1,NULL
,'p'},
1133 { "pass",1,NULL
,'p'},
1134 { "credentials",1,NULL
,'c'},
1135 { "port",1,NULL
,'P'},
1136 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1137 { NULL
, 0, NULL
, 0 }
1140 /* convert a string to uppercase. return false if the string
1141 * wasn't ASCII. Return success on a NULL ptr */
1143 uppercase_string(char *string
)
1149 /* check for unicode */
1150 if ((unsigned char) string
[0] & 0x80)
1152 *string
= toupper((unsigned char) *string
);
1159 static void print_cifs_mount_version(void)
1161 printf("mount.cifs version: %s.%s%s\n",
1162 MOUNT_CIFS_VERSION_MAJOR
,
1163 MOUNT_CIFS_VERSION_MINOR
,
1164 MOUNT_CIFS_VENDOR_SUFFIX
);
1167 int main(int argc
, char ** argv
)
1170 unsigned long flags
= MS_MANDLOCK
;
1171 char * orgoptions
= NULL
;
1172 char * share_name
= NULL
;
1173 const char * ipaddr
= NULL
;
1175 char * mountpoint
= NULL
;
1176 char * options
= NULL
;
1178 char * resolved_path
= NULL
;
1189 size_t options_size
= 0;
1191 int retry
= 0; /* set when we have to retry mount with uppercase */
1192 struct addrinfo
*addrhead
= NULL
, *addr
;
1193 struct utsname sysinfo
;
1194 struct mntent mountent
;
1195 struct sockaddr_in
*addr4
;
1196 struct sockaddr_in6
*addr6
;
1199 /* setlocale(LC_ALL, "");
1200 bindtextdomain(PACKAGE, LOCALEDIR);
1201 textdomain(PACKAGE); */
1204 thisprogram
= argv
[0];
1206 mount_cifs_usage(stderr
);
1208 if(thisprogram
== NULL
)
1209 thisprogram
= "mount.cifs";
1212 /* BB add workstation name and domain and pass down */
1214 /* #ifdef _GNU_SOURCE
1215 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1219 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1220 if (share_name
== NULL
) {
1221 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1224 mountpoint
= argv
[2];
1225 } else if (argc
== 2) {
1226 if ((strcmp(argv
[1], "-V") == 0) ||
1227 (strcmp(argv
[1], "--version") == 0))
1229 print_cifs_mount_version();
1233 if ((strcmp(argv
[1], "-h") == 0) ||
1234 (strcmp(argv
[1], "-?") == 0) ||
1235 (strcmp(argv
[1], "--help") == 0))
1236 mount_cifs_usage(stdout
);
1238 mount_cifs_usage(stderr
);
1240 mount_cifs_usage(stderr
);
1244 /* add sharename in opts string as unc= parm */
1245 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1246 longopts
, NULL
)) != -1) {
1248 /* No code to do the following options yet */
1250 list_with_volumelabel = 1;
1253 volumelabel = optarg;
1260 case 'h': /* help */
1261 mount_cifs_usage(stdout
);
1270 "option 'b' (MS_BIND) not supported\n");
1278 "option 'm' (MS_MOVE) not supported\n");
1282 orgoptions
= strdup(optarg
);
1284 case 'r': /* mount readonly */
1294 print_cifs_mount_version();
1297 flags
&= ~MS_RDONLY
;
1300 rsize
= atoi(optarg
) ;
1303 wsize
= atoi(optarg
);
1306 if (isdigit(*optarg
)) {
1309 uid
= strtoul(optarg
, &ep
, 10);
1311 fprintf(stderr
, "bad uid value \"%s\"\n", optarg
);
1317 if (!(pw
= getpwnam(optarg
))) {
1318 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1326 if (isdigit(*optarg
)) {
1329 gid
= strtoul(optarg
, &ep
, 10);
1331 fprintf(stderr
, "bad gid value \"%s\"\n", optarg
);
1337 if (!(gr
= getgrnam(optarg
))) {
1338 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1350 domain_name
= optarg
; /* BB fix this - currently ignored */
1354 if(mountpassword
== NULL
)
1355 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1358 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1362 get_password_from_file(0 /* stdin */,NULL
);
1370 fprintf(stderr
, "unknown mount option %c\n",c
);
1371 mount_cifs_usage(stderr
);
1375 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1376 mount_cifs_usage(stderr
);
1379 /* make sure mountpoint is legit */
1380 rc
= check_mountpoint(thisprogram
, mountpoint
);
1384 /* sanity check for unprivileged mounts */
1386 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1391 /* enable any default user mount flags */
1392 flags
|= CIFS_SETUID_FLAGS
;
1395 if (getenv("PASSWD")) {
1396 if(mountpassword
== NULL
)
1397 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1399 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1402 } else if (getenv("PASSWD_FD")) {
1403 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1404 } else if (getenv("PASSWD_FILE")) {
1405 get_password_from_file(0, getenv("PASSWD_FILE"));
1408 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1414 #if !CIFS_LEGACY_SETUID_CHECK
1415 if (!(flags
& (MS_USERS
|MS_USER
))) {
1416 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1420 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1423 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1424 "CIFS mounts not supported.",
1431 flags
&= ~(MS_USERS
|MS_USER
);
1433 addrhead
= addr
= parse_server(&share_name
);
1434 if((addrhead
== NULL
) && (got_ip
== 0)) {
1435 fprintf(stderr
, "No ip address specified and hostname not found\n");
1440 /* BB save off path and pop after mount returns? */
1441 resolved_path
= (char *)malloc(PATH_MAX
+1);
1443 /* Note that if we can not canonicalize the name, we get
1444 another chance to see if it is valid when we chdir to it */
1445 if (realpath(mountpoint
, resolved_path
)) {
1446 mountpoint
= resolved_path
;
1450 /* Note that the password will not be retrieved from the
1451 USER env variable (ie user%password form) as there is
1452 already a PASSWD environment varaible */
1454 user_name
= strdup(getenv("USER"));
1455 if (user_name
== NULL
)
1456 user_name
= getusername();
1460 if(got_password
== 0) {
1461 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1462 no good replacement yet. */
1463 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1464 if (!tmp_pass
|| !mountpassword
) {
1465 fprintf(stderr
, "Password not entered, exiting\n");
1468 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1471 /* FIXME launch daemon (handles dfs name resolution and credential change)
1472 remember to clear parms and overwrite password field before launching */
1474 optlen
= strlen(orgoptions
);
1479 optlen
+= strlen(share_name
) + 4;
1481 fprintf(stderr
, "No server share name specified\n");
1482 fprintf(stderr
, "\nMounting the DFS root for server not implemented yet\n");
1486 optlen
+= strlen(user_name
) + 6;
1487 optlen
+= MAX_ADDRESS_LEN
+ 4;
1489 optlen
+= strlen(mountpassword
) + 6;
1492 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1493 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 */);
1495 if(options
== NULL
) {
1496 fprintf(stderr
, "Could not allocate memory for mount options\n");
1500 strlcpy(options
, "unc=", options_size
);
1501 strlcat(options
,share_name
,options_size
);
1502 /* scan backwards and reverse direction of slash */
1503 temp
= strrchr(options
, '/');
1504 if(temp
> options
+ 6)
1507 /* check for syntax like user=domain\user */
1509 domain_name
= check_for_domain(&user_name
);
1510 strlcat(options
,",user=",options_size
);
1511 strlcat(options
,user_name
,options_size
);
1515 /* extra length accounted for in option string above */
1516 strlcat(options
,",domain=",options_size
);
1517 strlcat(options
,domain_name
,options_size
);
1521 strlcat(options
,",ver=",options_size
);
1522 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1525 strlcat(options
,",",options_size
);
1526 strlcat(options
,orgoptions
,options_size
);
1529 strlcat(options
,",prefixpath=",options_size
);
1530 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1533 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1534 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1536 if (!got_ip
&& addr
) {
1537 strlcat(options
, ",ip=", options_size
);
1538 current_len
= strnlen(options
, options_size
);
1539 optionstail
= options
+ current_len
;
1540 switch (addr
->ai_addr
->sa_family
) {
1542 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1543 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1544 options_size
- current_len
);
1547 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1548 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1549 options_size
- current_len
);
1555 /* if the address looks bogus, try the next one */
1557 addr
= addr
->ai_next
;
1565 if (addr
->ai_addr
->sa_family
== AF_INET6
&& addr6
->sin6_scope_id
) {
1566 strlcat(options
, "%", options_size
);
1567 current_len
= strnlen(options
, options_size
);
1568 optionstail
= options
+ current_len
;
1569 snprintf(optionstail
, options_size
- current_len
, "%u",
1570 addr6
->sin6_scope_id
);
1574 fprintf(stderr
, "\nmount.cifs kernel mount options: %s", options
);
1576 if (mountpassword
) {
1578 * Commas have to be doubled, or else they will
1579 * look like the parameter separator
1582 check_for_comma(&mountpassword
);
1583 strlcat(options
,",pass=",options_size
);
1584 strlcat(options
,mountpassword
,options_size
);
1586 fprintf(stderr
, ",pass=********");
1590 fprintf(stderr
, "\n");
1592 if (!fakemnt
&& mount(dev_name
, mountpoint
, cifs_fstype
, flags
, options
)) {
1597 addr
= addr
->ai_next
;
1603 fprintf(stderr
, "mount error: cifs filesystem not supported by the system\n");
1608 if (uppercase_string(dev_name
) &&
1609 uppercase_string(share_name
) &&
1610 uppercase_string(prefixpath
)) {
1611 fprintf(stderr
, "retrying with upper case share name\n");
1616 fprintf(stderr
, "mount error(%d): %s\n", errno
, strerror(errno
));
1617 fprintf(stderr
, "Refer to the mount.cifs(8) manual page (e.g. man "
1625 atexit(unlock_mtab
);
1628 fprintf(stderr
, "cannot lock mtab");
1631 pmntfile
= setmntent(MOUNTED
, "a+");
1633 fprintf(stderr
, "could not update mount table\n");
1638 mountent
.mnt_fsname
= dev_name
;
1639 mountent
.mnt_dir
= mountpoint
;
1640 mountent
.mnt_type
= (char *)(void *)cifs_fstype
;
1641 mountent
.mnt_opts
= (char *)malloc(220);
1642 if(mountent
.mnt_opts
) {
1643 char * mount_user
= getusername();
1644 memset(mountent
.mnt_opts
,0,200);
1645 if(flags
& MS_RDONLY
)
1646 strlcat(mountent
.mnt_opts
,"ro",220);
1648 strlcat(mountent
.mnt_opts
,"rw",220);
1649 if(flags
& MS_MANDLOCK
)
1650 strlcat(mountent
.mnt_opts
,",mand",220);
1651 if(flags
& MS_NOEXEC
)
1652 strlcat(mountent
.mnt_opts
,",noexec",220);
1653 if(flags
& MS_NOSUID
)
1654 strlcat(mountent
.mnt_opts
,",nosuid",220);
1655 if(flags
& MS_NODEV
)
1656 strlcat(mountent
.mnt_opts
,",nodev",220);
1657 if(flags
& MS_SYNCHRONOUS
)
1658 strlcat(mountent
.mnt_opts
,",sync",220);
1661 strlcat(mountent
.mnt_opts
,
1663 strlcat(mountent
.mnt_opts
,
1668 mountent
.mnt_freq
= 0;
1669 mountent
.mnt_passno
= 0;
1670 rc
= addmntent(pmntfile
,&mountent
);
1671 endmntent(pmntfile
);
1673 SAFE_FREE(mountent
.mnt_opts
);
1678 int len
= strlen(mountpassword
);
1679 memset(mountpassword
,0,len
);
1680 SAFE_FREE(mountpassword
);
1684 freeaddrinfo(addrhead
);
1686 SAFE_FREE(orgoptions
);
1687 SAFE_FREE(resolved_path
);
1688 SAFE_FREE(share_name
);