2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <sys/types.h>
30 #include <sys/mount.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "14"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
50 #include "include/version.h"
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 64
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * mount.cifs has been the subject of many "security" bugs that have arisen
93 * because of users and distributions installing it as a setuid root program.
94 * mount.cifs has not been audited for security. Thus, we strongly recommend
95 * that it not be installed setuid root. To make that abundantly clear,
96 * mount.cifs now check whether it's running setuid root and exit with an
97 * error if it is. If you wish to disable this check, then set the following
98 * #define to 1, but please realize that you do so at your own peril.
100 #define CIFS_DISABLE_SETUID_CHECK 0
103 * By default, mount.cifs follows the conventions set forth by /bin/mount
104 * for user mounts. That is, it requires that the mount be listed in
105 * /etc/fstab with the "user" option when run as an unprivileged user and
106 * mount.cifs is setuid root.
108 * Older versions of mount.cifs however were "looser" in this regard. When
109 * made setuid root, a user could run mount.cifs directly and mount any share
110 * on a directory owned by that user.
112 * The legacy behavior is now disabled by default. To reenable it, set the
113 * following #define to true.
115 #define CIFS_LEGACY_SETUID_CHECK 0
118 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
119 * flags by default. These defaults can be changed here.
121 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
123 const char *thisprogram
;
126 static int got_password
= 0;
127 static int got_user
= 0;
128 static int got_domain
= 0;
129 static int got_ip
= 0;
130 static int got_unc
= 0;
131 static int got_uid
= 0;
132 static int got_gid
= 0;
133 static char * user_name
= NULL
;
134 static char * mountpassword
= NULL
;
135 char * domain_name
= NULL
;
136 char * prefixpath
= NULL
;
138 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
139 * don't link to libreplace so need them here. */
141 /* like strncpy but does not 0 fill the buffer and always null
142 * terminates. bufsize is the size of the destination buffer */
145 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
147 size_t len
= strlen(s
);
149 if (bufsize
<= 0) return 0;
150 if (len
>= bufsize
) len
= bufsize
-1;
157 /* like strncat but does not 0 fill the buffer and always null
158 * terminates. bufsize is the length of the buffer, which should
159 * be one more than the maximum resulting string length */
162 static size_t strlcat(char *d
, const char *s
, size_t bufsize
)
164 size_t len1
= strlen(d
);
165 size_t len2
= strlen(s
);
166 size_t ret
= len1
+ len2
;
168 if (len1
+len2
>= bufsize
) {
169 if (bufsize
< (len1
+1)) {
172 len2
= bufsize
- (len1
+1);
175 memcpy(d
+len1
, s
, len2
);
183 * If an unprivileged user is doing the mounting then we need to ensure
184 * that the entry is in /etc/fstab.
187 check_mountpoint(const char *progname
, char *mountpoint
)
192 /* does mountpoint exist and is it a directory? */
193 err
= stat(".", &statbuf
);
195 fprintf(stderr
, "%s: failed to stat %s: %s\n", progname
,
196 mountpoint
, strerror(errno
));
200 if (!S_ISDIR(statbuf
.st_mode
)) {
201 fprintf(stderr
, "%s: %s is not a directory!", progname
,
206 #if CIFS_LEGACY_SETUID_CHECK
207 /* do extra checks on mountpoint for legacy setuid behavior */
208 if (!getuid() || geteuid())
211 if (statbuf
.st_uid
!= getuid()) {
212 fprintf(stderr
, "%s: %s is not owned by user\n", progname
,
217 if ((statbuf
.st_mode
& S_IRWXU
) != S_IRWXU
) {
218 fprintf(stderr
, "%s: invalid permissions on %s\n", progname
,
222 #endif /* CIFS_LEGACY_SETUID_CHECK */
227 #if CIFS_DISABLE_SETUID_CHECK
233 #else /* CIFS_DISABLE_SETUID_CHECK */
237 if (getuid() && !geteuid()) {
238 printf("This mount.cifs program has been built with the "
239 "ability to run as a setuid root program disabled.\n"
240 "mount.cifs has not been well audited for security "
241 "holes. Therefore the Samba team does not recommend "
242 "installing it as a setuid root program.\n");
248 #endif /* CIFS_DISABLE_SETUID_CHECK */
250 #if CIFS_LEGACY_SETUID_CHECK
252 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
257 #else /* CIFS_LEGACY_SETUID_CHECK */
259 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
265 /* make sure this mount is listed in /etc/fstab */
266 fstab
= setmntent(_PATH_FSTAB
, "r");
268 fprintf(stderr
, "Couldn't open %s for reading!\n",
273 while((mnt
= getmntent(fstab
))) {
274 if (!strcmp(mountpoint
, mnt
->mnt_dir
))
279 if (mnt
== NULL
|| strcmp(mnt
->mnt_fsname
, devname
)) {
280 fprintf(stderr
, "%s: permission denied: no match for "
281 "%s found in %s\n", progname
, mountpoint
,
287 * 'mount' munges the options from fstab before passing them
288 * to us. It is non-trivial to test that we have the correct
289 * set of options. We don't want to trust what the user
290 * gave us, so just take whatever is in /etc/fstab.
293 *options
= strdup(mnt
->mnt_opts
);
296 #endif /* CIFS_LEGACY_SETUID_CHECK */
301 open nofollow - avoid symlink exposure?
302 get owner of dir see if matches self or if root
303 call system(umount argv) etc.
307 static char * check_for_domain(char **);
310 static void mount_cifs_usage(void)
312 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
313 printf("\nMount the remote target, specified as a UNC name,");
314 printf(" to a local directory.\n\nOptions:\n");
315 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
316 printf("\nLess commonly used options:");
317 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
318 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
319 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
320 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
321 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
322 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
323 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
324 printf("\n\nRarely used options:");
325 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
326 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
327 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
328 printf("\n\tin6_addr");
329 printf("\n\nOptions are described in more detail in the manual page");
330 printf("\n\tman 8 mount.cifs\n");
331 printf("\nTo display the version number of the mount helper:");
332 printf("\n\t%s -V\n",thisprogram
);
334 SAFE_FREE(mountpassword
);
337 /* caller frees username if necessary */
338 static char * getusername(void) {
339 char *username
= NULL
;
340 struct passwd
*password
= getpwuid(getuid());
343 username
= password
->pw_name
;
348 static int open_cred_file(char * file_name
)
355 i
= access(file_name
, R_OK
);
359 fs
= fopen(file_name
,"r");
362 line_buf
= (char *)malloc(4096);
363 if(line_buf
== NULL
) {
368 while(fgets(line_buf
,4096,fs
)) {
369 /* parse line from credential file */
371 /* eat leading white space */
372 for(i
=0;i
<4086;i
++) {
373 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
375 /* if whitespace - skip past it */
377 if (strncasecmp("username",line_buf
+i
,8) == 0) {
378 temp_val
= strchr(line_buf
+ i
,'=');
380 /* go past equals sign */
382 for(length
= 0;length
<4087;length
++) {
383 if ((temp_val
[length
] == '\n')
384 || (temp_val
[length
] == '\0')) {
385 temp_val
[length
] = '\0';
390 printf("mount.cifs failed due to malformed username in credentials file");
391 memset(line_buf
,0,4096);
395 user_name
= (char *)calloc(1 + length
,1);
396 /* BB adding free of user_name string before exit,
397 not really necessary but would be cleaner */
398 strlcpy(user_name
,temp_val
, length
+1);
401 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
402 temp_val
= strchr(line_buf
+i
,'=');
404 /* go past equals sign */
406 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
407 if ((temp_val
[length
] == '\n')
408 || (temp_val
[length
] == '\0')) {
409 temp_val
[length
] = '\0';
413 if(length
> MOUNT_PASSWD_SIZE
) {
414 printf("mount.cifs failed: password in credentials file too long\n");
415 memset(line_buf
,0, 4096);
418 if(mountpassword
== NULL
) {
419 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
421 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
423 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
428 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
429 temp_val
= strchr(line_buf
+i
,'=');
431 /* go past equals sign */
434 printf("\nDomain %s\n",temp_val
);
435 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
436 if ((temp_val
[length
] == '\n')
437 || (temp_val
[length
] == '\0')) {
438 temp_val
[length
] = '\0';
442 if(length
> DOMAIN_SIZE
) {
443 printf("mount.cifs failed: domain in credentials file too long\n");
446 if(domain_name
== NULL
) {
447 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
449 memset(domain_name
,0,DOMAIN_SIZE
);
451 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
464 static int get_password_from_file(int file_descript
, char * filename
)
470 if(mountpassword
== NULL
)
471 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
473 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
475 if (mountpassword
== NULL
) {
476 printf("malloc failed\n");
480 if(filename
!= NULL
) {
481 rc
= access(filename
, R_OK
);
483 fprintf(stderr
, "mount.cifs failed: access check of %s failed: %s\n",
484 filename
, strerror(errno
));
487 file_descript
= open(filename
, O_RDONLY
);
488 if(file_descript
< 0) {
489 printf("mount.cifs failed. %s attempting to open password file %s\n",
490 strerror(errno
),filename
);
494 /* else file already open and fd provided */
496 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
497 rc
= read(file_descript
,&c
,1);
499 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno
));
501 close(file_descript
);
504 if(mountpassword
[0] == 0) {
506 printf("\nWarning: null password used since cifs password file empty");
509 } else /* read valid character */ {
510 if((c
== 0) || (c
== '\n')) {
511 mountpassword
[i
] = '\0';
514 mountpassword
[i
] = c
;
517 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
518 printf("\nWarning: password longer than %d characters specified in cifs password file",
522 if(filename
!= NULL
) {
523 close(file_descript
);
529 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
532 char * percent_char
= NULL
;
534 char * next_keyword
= NULL
;
542 if (!optionsp
|| !*optionsp
)
546 /* BB fixme check for separator override BB */
550 snprintf(user
,sizeof(user
),"%u",getuid());
552 snprintf(group
,sizeof(group
),"%u",getgid());
555 /* while ((data = strsep(&options, ",")) != NULL) { */
556 while(data
!= NULL
) {
557 /* check if ends with trailing comma */
561 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
562 /* data = next keyword */
563 /* value = next value ie stuff after equal sign */
565 next_keyword
= strchr(data
,','); /* BB handle sep= */
567 /* temporarily null terminate end of keyword=value pair */
571 /* temporarily null terminate keyword to make keyword and value distinct */
572 if ((value
= strchr(data
, '=')) != NULL
) {
577 if (strncmp(data
, "users",5) == 0) {
578 if(!value
|| !*value
) {
579 *filesys_flags
|= MS_USERS
;
582 } else if (strncmp(data
, "user_xattr",10) == 0) {
583 /* do nothing - need to skip so not parsed as user name */
584 } else if (strncmp(data
, "user", 4) == 0) {
586 if (!value
|| !*value
) {
587 if(data
[4] == '\0') {
588 *filesys_flags
|= MS_USER
;
591 printf("username specified with no parameter\n");
593 return 1; /* needs_arg; */
596 if (strnlen(value
, 260) < 260) {
598 percent_char
= strchr(value
,'%');
601 if(mountpassword
== NULL
)
602 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
605 printf("\nmount.cifs warning - password specified twice\n");
608 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
609 /* remove password from username */
610 while(*percent_char
!= 0) {
616 /* this is only case in which the user
617 name buf is not malloc - so we have to
618 check for domain name embedded within
619 the user name here since the later
620 call to check_for_domain will not be
622 domain_name
= check_for_domain(&value
);
624 printf("username too long\n");
629 } else if (strncmp(data
, "pass", 4) == 0) {
630 if (!value
|| !*value
) {
632 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
635 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
637 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
639 mountpassword
= strndup(value
, MOUNT_PASSWD_SIZE
);
640 if (!mountpassword
) {
641 fprintf(stderr
, "mount.cifs error: %s", strerror(ENOMEM
));
648 fprintf(stderr
, "password too long\n");
653 } else if (strncmp(data
, "sec", 3) == 0) {
655 if (!strncmp(value
, "none", 4) ||
656 !strncmp(value
, "krb5", 4))
659 } else if (strncmp(data
, "ip", 2) == 0) {
660 if (!value
|| !*value
) {
661 printf("target ip address argument missing");
662 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
664 printf("ip address %s override specified\n",value
);
667 printf("ip address too long\n");
671 } else if ((strncmp(data
, "unc", 3) == 0)
672 || (strncmp(data
, "target", 6) == 0)
673 || (strncmp(data
, "path", 4) == 0)) {
674 if (!value
|| !*value
) {
675 printf("invalid path to network resource\n");
677 return 1; /* needs_arg; */
678 } else if(strnlen(value
,5) < 5) {
679 printf("UNC name too short");
682 if (strnlen(value
, 300) < 300) {
684 if (strncmp(value
, "//", 2) == 0) {
686 printf("unc name specified twice, ignoring second\n");
689 } else if (strncmp(value
, "\\\\", 2) != 0) {
690 printf("UNC Path does not begin with // or \\\\ \n");
695 printf("unc name specified twice, ignoring second\n");
700 printf("CIFS: UNC name too long\n");
704 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
705 || (strncmp(data
, "workg", 5) == 0)) {
706 /* note this allows for synonyms of "domain"
707 such as "DOM" and "dom" and "workgroup"
708 and "WORKGRP" etc. */
709 if (!value
|| !*value
) {
710 printf("CIFS: invalid domain name\n");
712 return 1; /* needs_arg; */
714 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
717 printf("domain name too long\n");
721 } else if (strncmp(data
, "cred", 4) == 0) {
722 if (value
&& *value
) {
723 rc
= open_cred_file(value
);
725 printf("error %d (%s) opening credential file %s\n",
726 rc
, strerror(rc
), value
);
731 printf("invalid credential file name specified\n");
735 } else if (strncmp(data
, "uid", 3) == 0) {
736 if (value
&& *value
) {
738 if (!isdigit(*value
)) {
741 if (!(pw
= getpwnam(value
))) {
742 printf("bad user name \"%s\"\n", value
);
745 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
747 strlcpy(user
,value
,sizeof(user
));
751 } else if (strncmp(data
, "gid", 3) == 0) {
752 if (value
&& *value
) {
754 if (!isdigit(*value
)) {
757 if (!(gr
= getgrnam(value
))) {
758 printf("bad group name \"%s\"\n", value
);
761 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
763 strlcpy(group
,value
,sizeof(group
));
767 /* fmask and dmask synonyms for people used to smbfs syntax */
768 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
769 if (!value
|| !*value
) {
770 printf ("Option '%s' requires a numerical argument\n", data
);
775 if (value
[0] != '0') {
776 printf ("WARNING: '%s' not expressed in octal.\n", data
);
779 if (strcmp (data
, "fmask") == 0) {
780 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
781 data
= "file_mode"; /* BB fix this */
783 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
784 if (!value
|| !*value
) {
785 printf ("Option '%s' requires a numerical argument\n", data
);
790 if (value
[0] != '0') {
791 printf ("WARNING: '%s' not expressed in octal.\n", data
);
794 if (strcmp (data
, "dmask") == 0) {
795 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
798 /* the following eight mount options should be
799 stripped out from what is passed into the kernel
800 since these eight options are best passed as the
801 mount flags rather than redundantly to the kernel
802 and could generate spurious warnings depending on the
803 level of the corresponding cifs vfs kernel code */
804 } else if (strncmp(data
, "nosuid", 6) == 0) {
805 *filesys_flags
|= MS_NOSUID
;
806 } else if (strncmp(data
, "suid", 4) == 0) {
807 *filesys_flags
&= ~MS_NOSUID
;
808 } else if (strncmp(data
, "nodev", 5) == 0) {
809 *filesys_flags
|= MS_NODEV
;
810 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
811 (strncmp(data
, "nolock", 6) == 0)) {
812 *filesys_flags
&= ~MS_MANDLOCK
;
813 } else if (strncmp(data
, "dev", 3) == 0) {
814 *filesys_flags
&= ~MS_NODEV
;
815 } else if (strncmp(data
, "noexec", 6) == 0) {
816 *filesys_flags
|= MS_NOEXEC
;
817 } else if (strncmp(data
, "exec", 4) == 0) {
818 *filesys_flags
&= ~MS_NOEXEC
;
819 } else if (strncmp(data
, "guest", 5) == 0) {
820 user_name
= (char *)calloc(1, 1);
823 } else if (strncmp(data
, "ro", 2) == 0) {
824 *filesys_flags
|= MS_RDONLY
;
825 } else if (strncmp(data
, "rw", 2) == 0) {
826 *filesys_flags
&= ~MS_RDONLY
;
827 } else if (strncmp(data
, "remount", 7) == 0) {
828 *filesys_flags
|= MS_REMOUNT
;
829 } /* else if (strnicmp(data, "port", 4) == 0) {
830 if (value && *value) {
832 simple_strtoul(value, &value, 0);
834 } else if (strnicmp(data, "rsize", 5) == 0) {
835 if (value && *value) {
837 simple_strtoul(value, &value, 0);
839 } else if (strnicmp(data, "wsize", 5) == 0) {
840 if (value && *value) {
842 simple_strtoul(value, &value, 0);
844 } else if (strnicmp(data, "version", 3) == 0) {
846 printf("CIFS: Unknown mount option %s\n",data);
847 } */ /* nothing to do on those four mount options above.
848 Just pass to kernel and ignore them here */
850 /* Copy (possibly modified) option to out */
851 word_len
= strlen(data
);
853 word_len
+= 1 + strlen(value
);
855 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
862 strlcat(out
, ",", out_len
+ word_len
+ 2);
867 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
869 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
870 out_len
= strlen(out
);
876 /* special-case the uid and gid */
878 word_len
= strlen(user
);
880 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
887 strlcat(out
, ",", out_len
+ word_len
+ 6);
890 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
891 out_len
= strlen(out
);
894 word_len
= strlen(group
);
896 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
903 strlcat(out
, ",", out_len
+ word_len
+ 6);
906 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
907 out_len
= strlen(out
);
910 SAFE_FREE(*optionsp
);
915 /* replace all (one or more) commas with double commas */
916 static void check_for_comma(char ** ppasswrd
)
921 int number_of_commas
= 0;
936 if(number_of_commas
== 0)
938 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
939 /* would otherwise overflow the mount options buffer */
940 printf("\nInvalid password. Password contains too many commas.\n");
944 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
945 if(new_pass_buf
== NULL
)
948 for(i
=0,j
=0;i
<len
;i
++,j
++) {
949 new_pass_buf
[j
] = pass
[i
];
952 new_pass_buf
[j
] = pass
[i
];
955 new_pass_buf
[len
+number_of_commas
] = 0;
957 SAFE_FREE(*ppasswrd
);
958 *ppasswrd
= new_pass_buf
;
963 /* Usernames can not have backslash in them and we use
964 [BB check if usernames can have forward slash in them BB]
965 backslash as domain\user separator character
967 static char * check_for_domain(char **ppuser
)
969 char * original_string
;
979 original_string
= *ppuser
;
981 if (original_string
== NULL
)
984 original_len
= strlen(original_string
);
986 usernm
= strchr(*ppuser
,'/');
987 if (usernm
== NULL
) {
988 usernm
= strchr(*ppuser
,'\\');
994 printf("Domain name specified twice. Username probably malformed\n");
1000 if (domainnm
[0] != 0) {
1003 printf("null domain\n");
1005 len
= strlen(domainnm
);
1006 /* reset domainm to new buffer, and copy
1007 domain name into it */
1008 domainnm
= (char *)malloc(len
+1);
1009 if(domainnm
== NULL
)
1012 strlcpy(domainnm
,*ppuser
,len
+1);
1014 /* move_string(*ppuser, usernm+1) */
1015 len
= strlen(usernm
+1);
1017 if(len
>= original_len
) {
1018 /* should not happen */
1022 for(i
=0;i
<original_len
;i
++) {
1024 original_string
[i
] = usernm
[i
+1];
1025 else /* stuff with commas to remove last parm */
1026 original_string
[i
] = ',';
1029 /* BB add check for more than one slash?
1030 strchr(*ppuser,'/');
1031 strchr(*ppuser,'\\')
1037 /* replace all occurances of "from" in a string with "to" */
1038 static void replace_char(char *string
, char from
, char to
, int maxlen
)
1040 char *lastchar
= string
+ maxlen
;
1042 string
= strchr(string
, from
);
1045 if (string
>= lastchar
)
1051 /* Note that caller frees the returned buffer if necessary */
1052 static struct addrinfo
*
1053 parse_server(char ** punc_name
)
1055 char * unc_name
= *punc_name
;
1056 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1058 struct addrinfo
*addrlist
;
1061 if(length
> (MAX_UNC_LEN
- 1)) {
1062 printf("mount error: UNC name too long");
1065 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1066 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1067 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1072 /* BB add code to find DFS root here */
1073 printf("\nMounting the DFS root for domain not implemented yet\n");
1076 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1077 /* check for nfs syntax ie server:share */
1078 share
= strchr(unc_name
,':');
1080 *punc_name
= (char *)malloc(length
+3);
1081 if(*punc_name
== NULL
) {
1082 /* put the original string back if
1084 *punc_name
= unc_name
;
1088 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1089 SAFE_FREE(unc_name
);
1090 unc_name
= *punc_name
;
1091 unc_name
[length
+2] = 0;
1092 goto continue_unc_parsing
;
1094 printf("mount error: improperly formatted UNC name.");
1095 printf(" %s does not begin with \\\\ or //\n",unc_name
);
1099 continue_unc_parsing
:
1104 /* allow for either delimiter between host and sharename */
1105 if ((share
= strpbrk(unc_name
, "/\\"))) {
1106 *share
= 0; /* temporarily terminate the string */
1109 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1111 printf("mount error: could not resolve address for %s: %s\n",
1112 unc_name
, gai_strerror(rc
));
1116 *(share
- 1) = '/'; /* put delimiter back */
1118 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1119 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1120 *prefixpath
= 0; /* permanently terminate the string */
1121 if (!strlen(++prefixpath
))
1122 prefixpath
= NULL
; /* this needs to be done explicitly */
1126 printf("ip address specified explicitly\n");
1129 /* BB should we pass an alternate version of the share name as Unicode */
1133 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1134 printf("Mounting the DFS root for a particular server not implemented yet\n");
1141 static struct option longopts
[] = {
1142 { "all", 0, NULL
, 'a' },
1143 { "help",0, NULL
, 'h' },
1144 { "move",0, NULL
, 'm' },
1145 { "bind",0, NULL
, 'b' },
1146 { "read-only", 0, NULL
, 'r' },
1147 { "ro", 0, NULL
, 'r' },
1148 { "verbose", 0, NULL
, 'v' },
1149 { "version", 0, NULL
, 'V' },
1150 { "read-write", 0, NULL
, 'w' },
1151 { "rw", 0, NULL
, 'w' },
1152 { "options", 1, NULL
, 'o' },
1153 { "type", 1, NULL
, 't' },
1154 { "rsize",1, NULL
, 'R' },
1155 { "wsize",1, NULL
, 'W' },
1156 { "uid", 1, NULL
, '1'},
1157 { "gid", 1, NULL
, '2'},
1158 { "user",1,NULL
,'u'},
1159 { "username",1,NULL
,'u'},
1160 { "dom",1,NULL
,'d'},
1161 { "domain",1,NULL
,'d'},
1162 { "password",1,NULL
,'p'},
1163 { "pass",1,NULL
,'p'},
1164 { "credentials",1,NULL
,'c'},
1165 { "port",1,NULL
,'P'},
1166 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1167 { NULL
, 0, NULL
, 0 }
1170 /* convert a string to uppercase. return false if the string
1171 * wasn't ASCII. Return success on a NULL ptr */
1173 uppercase_string(char *string
)
1179 /* check for unicode */
1180 if ((unsigned char) string
[0] & 0x80)
1182 *string
= toupper((unsigned char) *string
);
1189 static void print_cifs_mount_version(void)
1191 printf("mount.cifs version: %s.%s%s\n",
1192 MOUNT_CIFS_VERSION_MAJOR
,
1193 MOUNT_CIFS_VERSION_MINOR
,
1194 MOUNT_CIFS_VENDOR_SUFFIX
);
1198 * This function borrowed from fuse-utils...
1200 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1201 * newlines embedded within the text fields. To make sure no one corrupts
1202 * the mtab, fail the mount if there are embedded newlines.
1204 static int check_newline(const char *progname
, const char *name
)
1207 for (s
= "\n"; *s
; s
++) {
1208 if (strchr(name
, *s
)) {
1209 fprintf(stderr
, "%s: illegal character 0x%02x in mount entry\n",
1217 static int check_mtab(const char *progname
, const char *devname
,
1220 if (check_newline(progname
, devname
) == -1 ||
1221 check_newline(progname
, dir
) == -1)
1227 int main(int argc
, char ** argv
)
1230 unsigned long flags
= MS_MANDLOCK
;
1231 char * orgoptions
= NULL
;
1232 char * share_name
= NULL
;
1233 const char * ipaddr
= NULL
;
1235 char * mountpoint
= NULL
;
1236 char * options
= NULL
;
1238 char * resolved_path
= NULL
;
1249 size_t options_size
= 0;
1251 int retry
= 0; /* set when we have to retry mount with uppercase */
1252 struct addrinfo
*addrhead
= NULL
, *addr
;
1253 struct utsname sysinfo
;
1254 struct mntent mountent
;
1255 struct sockaddr_in
*addr4
;
1256 struct sockaddr_in6
*addr6
;
1262 /* setlocale(LC_ALL, "");
1263 bindtextdomain(PACKAGE, LOCALEDIR);
1264 textdomain(PACKAGE); */
1267 thisprogram
= argv
[0];
1273 if(thisprogram
== NULL
)
1274 thisprogram
= "mount.cifs";
1277 /* BB add workstation name and domain and pass down */
1279 /* #ifdef _GNU_SOURCE
1280 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1284 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1285 if (share_name
== NULL
) {
1286 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1289 mountpoint
= argv
[2];
1290 } else if (argc
== 2) {
1291 if ((strcmp(argv
[1], "-V") == 0) ||
1292 (strcmp(argv
[1], "--version") == 0))
1294 print_cifs_mount_version();
1298 if ((strcmp(argv
[1], "-h") == 0) ||
1299 (strcmp(argv
[1], "-?") == 0) ||
1300 (strcmp(argv
[1], "--help") == 0))
1314 /* add sharename in opts string as unc= parm */
1315 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1316 longopts
, NULL
)) != -1) {
1318 /* No code to do the following options yet */
1320 list_with_volumelabel = 1;
1323 volumelabel = optarg;
1330 case 'h': /* help */
1331 mount_cifs_usage ();
1341 "option 'b' (MS_BIND) not supported\n");
1349 "option 'm' (MS_MOVE) not supported\n");
1353 orgoptions
= strdup(optarg
);
1355 case 'r': /* mount readonly */
1365 print_cifs_mount_version();
1368 flags
&= ~MS_RDONLY
;
1371 rsize
= atoi(optarg
) ;
1374 wsize
= atoi(optarg
);
1377 if (isdigit(*optarg
)) {
1380 uid
= strtoul(optarg
, &ep
, 10);
1382 printf("bad uid value \"%s\"\n", optarg
);
1388 if (!(pw
= getpwnam(optarg
))) {
1389 printf("bad user name \"%s\"\n", optarg
);
1397 if (isdigit(*optarg
)) {
1400 gid
= strtoul(optarg
, &ep
, 10);
1402 printf("bad gid value \"%s\"\n", optarg
);
1408 if (!(gr
= getgrnam(optarg
))) {
1409 printf("bad user name \"%s\"\n", optarg
);
1421 domain_name
= optarg
; /* BB fix this - currently ignored */
1425 if(mountpassword
== NULL
)
1426 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1429 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1433 get_password_from_file(0 /* stdin */,NULL
);
1441 printf("unknown mount option %c\n",c
);
1447 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1452 /* make sure mountpoint is legit */
1453 rc
= chdir(mountpoint
);
1455 fprintf(stderr
, "Couldn't chdir to %s: %s\n", mountpoint
,
1461 rc
= check_mountpoint(thisprogram
, mountpoint
);
1465 /* sanity check for unprivileged mounts */
1467 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1472 /* enable any default user mount flags */
1473 flags
|= CIFS_SETUID_FLAGS
;
1476 if (getenv("PASSWD")) {
1477 if(mountpassword
== NULL
)
1478 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1480 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1483 } else if (getenv("PASSWD_FD")) {
1484 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1485 } else if (getenv("PASSWD_FILE")) {
1486 get_password_from_file(0, getenv("PASSWD_FILE"));
1489 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1495 #if !CIFS_LEGACY_SETUID_CHECK
1496 if (!(flags
& (MS_USERS
|MS_USER
))) {
1497 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1501 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1504 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1505 "CIFS mounts not supported.",
1512 flags
&= ~(MS_USERS
|MS_USER
);
1514 addrhead
= addr
= parse_server(&share_name
);
1515 if((addrhead
== NULL
) && (got_ip
== 0)) {
1516 printf("No ip address specified and hostname not found\n");
1521 /* BB save off path and pop after mount returns? */
1522 resolved_path
= (char *)malloc(PATH_MAX
+1);
1523 if (!resolved_path
) {
1524 fprintf(stderr
, "Unable to allocate memory.\n");
1529 /* Note that if we can not canonicalize the name, we get
1530 another chance to see if it is valid when we chdir to it */
1531 if(!realpath(".", resolved_path
)) {
1532 fprintf(stderr
, "Unable to resolve %s to canonical path: %s\n",
1533 mountpoint
, strerror(errno
));
1538 mountpoint
= resolved_path
;
1541 /* Note that the password will not be retrieved from the
1542 USER env variable (ie user%password form) as there is
1543 already a PASSWD environment varaible */
1545 user_name
= strdup(getenv("USER"));
1546 if (user_name
== NULL
)
1547 user_name
= getusername();
1551 if(got_password
== 0) {
1552 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1553 no good replacement yet. */
1554 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1555 if (!tmp_pass
|| !mountpassword
) {
1556 printf("Password not entered, exiting\n");
1559 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1562 /* FIXME launch daemon (handles dfs name resolution and credential change)
1563 remember to clear parms and overwrite password field before launching */
1565 optlen
= strlen(orgoptions
);
1570 optlen
+= strlen(share_name
) + 4;
1572 printf("No server share name specified\n");
1573 printf("\nMounting the DFS root for server not implemented yet\n");
1577 optlen
+= strlen(user_name
) + 6;
1578 optlen
+= MAX_ADDRESS_LEN
+ 4;
1580 optlen
+= strlen(mountpassword
) + 6;
1583 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1584 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 */);
1586 if(options
== NULL
) {
1587 printf("Could not allocate memory for mount options\n");
1591 strlcpy(options
, "unc=", options_size
);
1592 strlcat(options
,share_name
,options_size
);
1593 /* scan backwards and reverse direction of slash */
1594 temp
= strrchr(options
, '/');
1595 if(temp
> options
+ 6)
1598 /* check for syntax like user=domain\user */
1600 domain_name
= check_for_domain(&user_name
);
1601 strlcat(options
,",user=",options_size
);
1602 strlcat(options
,user_name
,options_size
);
1606 /* extra length accounted for in option string above */
1607 strlcat(options
,",domain=",options_size
);
1608 strlcat(options
,domain_name
,options_size
);
1612 strlcat(options
,",ver=",options_size
);
1613 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1616 strlcat(options
,",",options_size
);
1617 strlcat(options
,orgoptions
,options_size
);
1620 strlcat(options
,",prefixpath=",options_size
);
1621 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1624 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1625 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1627 if (!got_ip
&& addr
) {
1628 strlcat(options
, ",ip=", options_size
);
1629 current_len
= strnlen(options
, options_size
);
1630 optionstail
= options
+ current_len
;
1631 switch (addr
->ai_addr
->sa_family
) {
1633 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1634 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1635 options_size
- current_len
);
1638 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1639 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1640 options_size
- current_len
);
1644 /* if the address looks bogus, try the next one */
1646 addr
= addr
->ai_next
;
1655 fprintf(stderr
, "\nmount.cifs kernel mount options: %s", options
);
1657 if (mountpassword
) {
1659 * Commas have to be doubled, or else they will
1660 * look like the parameter separator
1663 check_for_comma(&mountpassword
);
1664 strlcat(options
,",pass=",options_size
);
1665 strlcat(options
,mountpassword
,options_size
);
1667 fprintf(stderr
, ",pass=********");
1671 fprintf(stderr
, "\n");
1673 rc
= check_mtab(thisprogram
, dev_name
, mountpoint
);
1677 if (!fakemnt
&& mount(dev_name
, ".", "cifs", flags
, options
)) {
1682 addr
= addr
->ai_next
;
1688 printf("mount error: cifs filesystem not supported by the system\n");
1693 if (uppercase_string(dev_name
) &&
1694 uppercase_string(share_name
) &&
1695 uppercase_string(prefixpath
)) {
1696 printf("retrying with upper case share name\n");
1701 printf("mount error(%d): %s\n", errno
, strerror(errno
));
1702 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1710 atexit(unlock_mtab
);
1713 printf("cannot lock mtab");
1716 pmntfile
= setmntent(MOUNTED
, "a+");
1718 printf("could not update mount table\n");
1723 mountent
.mnt_fsname
= dev_name
;
1724 mountent
.mnt_dir
= mountpoint
;
1725 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1726 mountent
.mnt_opts
= (char *)malloc(220);
1727 if(mountent
.mnt_opts
) {
1728 char * mount_user
= getusername();
1729 memset(mountent
.mnt_opts
,0,200);
1730 if(flags
& MS_RDONLY
)
1731 strlcat(mountent
.mnt_opts
,"ro",220);
1733 strlcat(mountent
.mnt_opts
,"rw",220);
1734 if(flags
& MS_MANDLOCK
)
1735 strlcat(mountent
.mnt_opts
,",mand",220);
1736 if(flags
& MS_NOEXEC
)
1737 strlcat(mountent
.mnt_opts
,",noexec",220);
1738 if(flags
& MS_NOSUID
)
1739 strlcat(mountent
.mnt_opts
,",nosuid",220);
1740 if(flags
& MS_NODEV
)
1741 strlcat(mountent
.mnt_opts
,",nodev",220);
1742 if(flags
& MS_SYNCHRONOUS
)
1743 strlcat(mountent
.mnt_opts
,",sync",220);
1746 strlcat(mountent
.mnt_opts
,
1748 strlcat(mountent
.mnt_opts
,
1753 mountent
.mnt_freq
= 0;
1754 mountent
.mnt_passno
= 0;
1755 rc
= addmntent(pmntfile
,&mountent
);
1756 endmntent(pmntfile
);
1758 SAFE_FREE(mountent
.mnt_opts
);
1763 int len
= strlen(mountpassword
);
1764 memset(mountpassword
,0,len
);
1765 SAFE_FREE(mountpassword
);
1769 freeaddrinfo(addrhead
);
1771 SAFE_FREE(orgoptions
);
1772 SAFE_FREE(resolved_path
);
1773 SAFE_FREE(share_name
);