2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <sys/types.h>
30 #include <sys/mount.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "14"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
62 #include "include/config.h"
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS 0x40000000
75 #define MS_USER 0x80000000
77 #define MAX_UNC_LEN 1024
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 * 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(FILE *stream
)
312 fprintf(stream
, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
313 fprintf(stream
, "\nMount the remote target, specified as a UNC name,");
314 fprintf(stream
, " to a local directory.\n\nOptions:\n");
315 fprintf(stream
, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
316 fprintf(stream
, "\nLess commonly used options:");
317 fprintf(stream
, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
318 fprintf(stream
, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
319 fprintf(stream
, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
320 fprintf(stream
, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
321 fprintf(stream
, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
322 fprintf(stream
, "\n\t(e.g. unneeded for mounts to most Samba versions):");
323 fprintf(stream
, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
324 fprintf(stream
, "\n\nRarely used options:");
325 fprintf(stream
, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
326 fprintf(stream
, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
327 fprintf(stream
, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
328 fprintf(stream
, "\n\nOptions are described in more detail in the manual page");
329 fprintf(stream
, "\n\tman 8 mount.cifs\n");
330 fprintf(stream
, "\nTo display the version number of the mount helper:");
331 fprintf(stream
, "\n\t%s -V\n",thisprogram
);
333 SAFE_FREE(mountpassword
);
335 if (stream
== stderr
)
340 /* caller frees username if necessary */
341 static char * getusername(void) {
342 char *username
= NULL
;
343 struct passwd
*password
= getpwuid(getuid());
346 username
= password
->pw_name
;
351 static int open_cred_file(char * file_name
)
358 i
= access(file_name
, R_OK
);
362 fs
= fopen(file_name
,"r");
365 line_buf
= (char *)malloc(4096);
366 if(line_buf
== NULL
) {
371 while(fgets(line_buf
,4096,fs
)) {
372 /* parse line from credential file */
374 /* eat leading white space */
375 for(i
=0;i
<4086;i
++) {
376 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
378 /* if whitespace - skip past it */
380 if (strncasecmp("username",line_buf
+i
,8) == 0) {
381 temp_val
= strchr(line_buf
+ i
,'=');
383 /* go past equals sign */
385 for(length
= 0;length
<4087;length
++) {
386 if ((temp_val
[length
] == '\n')
387 || (temp_val
[length
] == '\0')) {
388 temp_val
[length
] = '\0';
393 fprintf(stderr
, "mount.cifs failed due to malformed username in credentials file\n");
394 memset(line_buf
,0,4096);
398 user_name
= (char *)calloc(1 + length
,1);
399 /* BB adding free of user_name string before exit,
400 not really necessary but would be cleaner */
401 strlcpy(user_name
,temp_val
, length
+1);
404 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
405 temp_val
= strchr(line_buf
+i
,'=');
407 /* go past equals sign */
409 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
410 if ((temp_val
[length
] == '\n')
411 || (temp_val
[length
] == '\0')) {
412 temp_val
[length
] = '\0';
416 if(length
> MOUNT_PASSWD_SIZE
) {
417 fprintf(stderr
, "mount.cifs failed: password in credentials file too long\n");
418 memset(line_buf
,0, 4096);
421 if(mountpassword
== NULL
) {
422 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
424 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
426 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
431 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
432 temp_val
= strchr(line_buf
+i
,'=');
434 /* go past equals sign */
437 fprintf(stderr
, "\nDomain %s\n",temp_val
);
438 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
439 if ((temp_val
[length
] == '\n')
440 || (temp_val
[length
] == '\0')) {
441 temp_val
[length
] = '\0';
445 if(length
> DOMAIN_SIZE
) {
446 fprintf(stderr
, "mount.cifs failed: domain in credentials file too long\n");
449 if(domain_name
== NULL
) {
450 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
452 memset(domain_name
,0,DOMAIN_SIZE
);
454 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
467 static int get_password_from_file(int file_descript
, char * filename
)
473 if(mountpassword
== NULL
)
474 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
476 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
478 if (mountpassword
== NULL
) {
479 fprintf(stderr
, "malloc failed\n");
483 if(filename
!= NULL
) {
484 rc
= access(filename
, R_OK
);
486 fprintf(stderr
, "mount.cifs failed: access check of %s failed: %s\n",
487 filename
, strerror(errno
));
490 file_descript
= open(filename
, O_RDONLY
);
491 if(file_descript
< 0) {
492 fprintf(stderr
, "mount.cifs failed. %s attempting to open password file %s\n",
493 strerror(errno
),filename
);
497 /* else file already open and fd provided */
499 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
500 rc
= read(file_descript
,&c
,1);
502 fprintf(stderr
, "mount.cifs failed. Error %s reading password file\n",strerror(errno
));
504 close(file_descript
);
507 if(mountpassword
[0] == 0) {
509 fprintf(stderr
, "\nWarning: null password used since cifs password file empty");
512 } else /* read valid character */ {
513 if((c
== 0) || (c
== '\n')) {
514 mountpassword
[i
] = '\0';
517 mountpassword
[i
] = c
;
520 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
521 fprintf(stderr
, "\nWarning: password longer than %d characters specified in cifs password file",
525 if(filename
!= NULL
) {
526 close(file_descript
);
532 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
535 char * percent_char
= NULL
;
537 char * next_keyword
= NULL
;
545 if (!optionsp
|| !*optionsp
)
549 /* BB fixme check for separator override BB */
553 snprintf(user
,sizeof(user
),"%u",getuid());
555 snprintf(group
,sizeof(group
),"%u",getgid());
558 /* while ((data = strsep(&options, ",")) != NULL) { */
559 while(data
!= NULL
) {
560 /* check if ends with trailing comma */
564 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
565 /* data = next keyword */
566 /* value = next value ie stuff after equal sign */
568 next_keyword
= strchr(data
,','); /* BB handle sep= */
570 /* temporarily null terminate end of keyword=value pair */
574 /* temporarily null terminate keyword to make keyword and value distinct */
575 if ((value
= strchr(data
, '=')) != NULL
) {
580 if (strncmp(data
, "users",5) == 0) {
581 if(!value
|| !*value
) {
582 *filesys_flags
|= MS_USERS
;
585 } else if (strncmp(data
, "user_xattr",10) == 0) {
586 /* do nothing - need to skip so not parsed as user name */
587 } else if (strncmp(data
, "user", 4) == 0) {
589 if (!value
|| !*value
) {
590 if(data
[4] == '\0') {
591 *filesys_flags
|= MS_USER
;
594 fprintf(stderr
, "username specified with no parameter\n");
596 return 1; /* needs_arg; */
599 if (strnlen(value
, 260) < 260) {
601 percent_char
= strchr(value
,'%');
604 if(mountpassword
== NULL
)
605 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
608 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
611 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
612 /* remove password from username */
613 while(*percent_char
!= 0) {
619 /* this is only case in which the user
620 name buf is not malloc - so we have to
621 check for domain name embedded within
622 the user name here since the later
623 call to check_for_domain will not be
625 domain_name
= check_for_domain(&value
);
627 fprintf(stderr
, "username too long\n");
632 } else if (strncmp(data
, "pass", 4) == 0) {
633 if (!value
|| !*value
) {
635 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
638 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
640 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
642 mountpassword
= strndup(value
, MOUNT_PASSWD_SIZE
);
643 if (!mountpassword
) {
644 fprintf(stderr
, "mount.cifs error: %s", strerror(ENOMEM
));
651 fprintf(stderr
, "password too long\n");
656 } else if (strncmp(data
, "sec", 3) == 0) {
658 if (!strncmp(value
, "none", 4) ||
659 !strncmp(value
, "krb5", 4))
662 } else if (strncmp(data
, "ip", 2) == 0) {
663 if (!value
|| !*value
) {
664 fprintf(stderr
, "target ip address argument missing");
665 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
667 fprintf(stderr
, "ip address %s override specified\n",value
);
670 fprintf(stderr
, "ip address too long\n");
674 } else if ((strncmp(data
, "unc", 3) == 0)
675 || (strncmp(data
, "target", 6) == 0)
676 || (strncmp(data
, "path", 4) == 0)) {
677 if (!value
|| !*value
) {
678 fprintf(stderr
, "invalid path to network resource\n");
680 return 1; /* needs_arg; */
681 } else if(strnlen(value
,5) < 5) {
682 fprintf(stderr
, "UNC name too short");
685 if (strnlen(value
, 300) < 300) {
687 if (strncmp(value
, "//", 2) == 0) {
689 fprintf(stderr
, "unc name specified twice, ignoring second\n");
692 } else if (strncmp(value
, "\\\\", 2) != 0) {
693 fprintf(stderr
, "UNC Path does not begin with // or \\\\ \n");
698 fprintf(stderr
, "unc name specified twice, ignoring second\n");
703 fprintf(stderr
, "CIFS: UNC name too long\n");
707 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
708 || (strncmp(data
, "workg", 5) == 0)) {
709 /* note this allows for synonyms of "domain"
710 such as "DOM" and "dom" and "workgroup"
711 and "WORKGRP" etc. */
712 if (!value
|| !*value
) {
713 fprintf(stderr
, "CIFS: invalid domain name\n");
715 return 1; /* needs_arg; */
717 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
720 fprintf(stderr
, "domain name too long\n");
724 } else if (strncmp(data
, "cred", 4) == 0) {
725 if (value
&& *value
) {
726 rc
= open_cred_file(value
);
728 fprintf(stderr
, "error %d (%s) opening credential file %s\n",
729 rc
, strerror(rc
), value
);
734 fprintf(stderr
, "invalid credential file name specified\n");
738 } else if (strncmp(data
, "uid", 3) == 0) {
739 if (value
&& *value
) {
741 if (!isdigit(*value
)) {
744 if (!(pw
= getpwnam(value
))) {
745 fprintf(stderr
, "bad user name \"%s\"\n", value
);
748 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
750 strlcpy(user
,value
,sizeof(user
));
754 } else if (strncmp(data
, "gid", 3) == 0) {
755 if (value
&& *value
) {
757 if (!isdigit(*value
)) {
760 if (!(gr
= getgrnam(value
))) {
761 fprintf(stderr
, "bad group name \"%s\"\n", value
);
764 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
766 strlcpy(group
,value
,sizeof(group
));
770 /* fmask and dmask synonyms for people used to smbfs syntax */
771 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
772 if (!value
|| !*value
) {
773 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
778 if (value
[0] != '0') {
779 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
782 if (strcmp (data
, "fmask") == 0) {
783 fprintf(stderr
, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
784 data
= "file_mode"; /* BB fix this */
786 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
787 if (!value
|| !*value
) {
788 fprintf(stderr
, "Option '%s' requires a numerical argument\n", data
);
793 if (value
[0] != '0') {
794 fprintf(stderr
, "WARNING: '%s' not expressed in octal.\n", data
);
797 if (strcmp (data
, "dmask") == 0) {
798 fprintf(stderr
, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
801 /* the following eight mount options should be
802 stripped out from what is passed into the kernel
803 since these eight options are best passed as the
804 mount flags rather than redundantly to the kernel
805 and could generate spurious warnings depending on the
806 level of the corresponding cifs vfs kernel code */
807 } else if (strncmp(data
, "nosuid", 6) == 0) {
808 *filesys_flags
|= MS_NOSUID
;
809 } else if (strncmp(data
, "suid", 4) == 0) {
810 *filesys_flags
&= ~MS_NOSUID
;
811 } else if (strncmp(data
, "nodev", 5) == 0) {
812 *filesys_flags
|= MS_NODEV
;
813 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
814 (strncmp(data
, "nolock", 6) == 0)) {
815 *filesys_flags
&= ~MS_MANDLOCK
;
816 } else if (strncmp(data
, "dev", 3) == 0) {
817 *filesys_flags
&= ~MS_NODEV
;
818 } else if (strncmp(data
, "noexec", 6) == 0) {
819 *filesys_flags
|= MS_NOEXEC
;
820 } else if (strncmp(data
, "exec", 4) == 0) {
821 *filesys_flags
&= ~MS_NOEXEC
;
822 } else if (strncmp(data
, "guest", 5) == 0) {
823 user_name
= (char *)calloc(1, 1);
826 } else if (strncmp(data
, "ro", 2) == 0) {
827 *filesys_flags
|= MS_RDONLY
;
829 } else if (strncmp(data
, "rw", 2) == 0) {
830 *filesys_flags
&= ~MS_RDONLY
;
832 } else if (strncmp(data
, "remount", 7) == 0) {
833 *filesys_flags
|= MS_REMOUNT
;
834 } /* else if (strnicmp(data, "port", 4) == 0) {
835 if (value && *value) {
837 simple_strtoul(value, &value, 0);
839 } else if (strnicmp(data, "rsize", 5) == 0) {
840 if (value && *value) {
842 simple_strtoul(value, &value, 0);
844 } else if (strnicmp(data, "wsize", 5) == 0) {
845 if (value && *value) {
847 simple_strtoul(value, &value, 0);
849 } else if (strnicmp(data, "version", 3) == 0) {
851 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
852 } */ /* nothing to do on those four mount options above.
853 Just pass to kernel and ignore them here */
855 /* Copy (possibly modified) option to out */
856 word_len
= strlen(data
);
858 word_len
+= 1 + strlen(value
);
860 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
867 strlcat(out
, ",", out_len
+ word_len
+ 2);
872 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
874 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
875 out_len
= strlen(out
);
881 /* special-case the uid and gid */
883 word_len
= strlen(user
);
885 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
892 strlcat(out
, ",", out_len
+ word_len
+ 6);
895 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
896 out_len
= strlen(out
);
899 word_len
= strlen(group
);
901 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
908 strlcat(out
, ",", out_len
+ word_len
+ 6);
911 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
912 out_len
= strlen(out
);
915 SAFE_FREE(*optionsp
);
920 /* replace all (one or more) commas with double commas */
921 static void check_for_comma(char ** ppasswrd
)
926 int number_of_commas
= 0;
941 if(number_of_commas
== 0)
943 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
944 /* would otherwise overflow the mount options buffer */
945 fprintf(stderr
, "\nInvalid password. Password contains too many commas.\n");
949 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
950 if(new_pass_buf
== NULL
)
953 for(i
=0,j
=0;i
<len
;i
++,j
++) {
954 new_pass_buf
[j
] = pass
[i
];
957 new_pass_buf
[j
] = pass
[i
];
960 new_pass_buf
[len
+number_of_commas
] = 0;
962 SAFE_FREE(*ppasswrd
);
963 *ppasswrd
= new_pass_buf
;
968 /* Usernames can not have backslash in them and we use
969 [BB check if usernames can have forward slash in them BB]
970 backslash as domain\user separator character
972 static char * check_for_domain(char **ppuser
)
974 char * original_string
;
984 original_string
= *ppuser
;
986 if (original_string
== NULL
)
989 original_len
= strlen(original_string
);
991 usernm
= strchr(*ppuser
,'/');
992 if (usernm
== NULL
) {
993 usernm
= strchr(*ppuser
,'\\');
999 fprintf(stderr
, "Domain name specified twice. Username probably malformed\n");
1005 if (domainnm
[0] != 0) {
1008 fprintf(stderr
, "null domain\n");
1010 len
= strlen(domainnm
);
1011 /* reset domainm to new buffer, and copy
1012 domain name into it */
1013 domainnm
= (char *)malloc(len
+1);
1014 if(domainnm
== NULL
)
1017 strlcpy(domainnm
,*ppuser
,len
+1);
1019 /* move_string(*ppuser, usernm+1) */
1020 len
= strlen(usernm
+1);
1022 if(len
>= original_len
) {
1023 /* should not happen */
1027 for(i
=0;i
<original_len
;i
++) {
1029 original_string
[i
] = usernm
[i
+1];
1030 else /* stuff with commas to remove last parm */
1031 original_string
[i
] = ',';
1034 /* BB add check for more than one slash?
1035 strchr(*ppuser,'/');
1036 strchr(*ppuser,'\\')
1042 /* replace all occurances of "from" in a string with "to" */
1043 static void replace_char(char *string
, char from
, char to
, int maxlen
)
1045 char *lastchar
= string
+ maxlen
;
1047 string
= strchr(string
, from
);
1050 if (string
>= lastchar
)
1056 /* Note that caller frees the returned buffer if necessary */
1057 static struct addrinfo
*
1058 parse_server(char ** punc_name
)
1060 char * unc_name
= *punc_name
;
1061 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1063 struct addrinfo
*addrlist
;
1066 if(length
> (MAX_UNC_LEN
- 1)) {
1067 fprintf(stderr
, "mount error: UNC name too long");
1070 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1071 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1072 fprintf(stderr
, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1077 /* BB add code to find DFS root here */
1078 fprintf(stderr
, "\nMounting the DFS root for domain not implemented yet\n");
1081 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1082 /* check for nfs syntax ie server:share */
1083 share
= strchr(unc_name
,':');
1085 *punc_name
= (char *)malloc(length
+3);
1086 if(*punc_name
== NULL
) {
1087 /* put the original string back if
1089 *punc_name
= unc_name
;
1093 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1094 SAFE_FREE(unc_name
);
1095 unc_name
= *punc_name
;
1096 unc_name
[length
+2] = 0;
1097 goto continue_unc_parsing
;
1099 fprintf(stderr
, "mount error: improperly formatted UNC name.");
1100 fprintf(stderr
, " %s does not begin with \\\\ or //\n",unc_name
);
1104 continue_unc_parsing
:
1109 /* allow for either delimiter between host and sharename */
1110 if ((share
= strpbrk(unc_name
, "/\\"))) {
1111 *share
= 0; /* temporarily terminate the string */
1114 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1116 fprintf(stderr
, "mount error: could not resolve address for %s: %s\n",
1117 unc_name
, gai_strerror(rc
));
1121 *(share
- 1) = '/'; /* put delimiter back */
1123 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1124 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1125 *prefixpath
= 0; /* permanently terminate the string */
1126 if (!strlen(++prefixpath
))
1127 prefixpath
= NULL
; /* this needs to be done explicitly */
1131 fprintf(stderr
, "ip address specified explicitly\n");
1134 /* BB should we pass an alternate version of the share name as Unicode */
1138 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1139 fprintf(stderr
, "Mounting the DFS root for a particular server not implemented yet\n");
1146 static struct option longopts
[] = {
1147 { "all", 0, NULL
, 'a' },
1148 { "help",0, NULL
, 'h' },
1149 { "move",0, NULL
, 'm' },
1150 { "bind",0, NULL
, 'b' },
1151 { "read-only", 0, NULL
, 'r' },
1152 { "ro", 0, NULL
, 'r' },
1153 { "verbose", 0, NULL
, 'v' },
1154 { "version", 0, NULL
, 'V' },
1155 { "read-write", 0, NULL
, 'w' },
1156 { "rw", 0, NULL
, 'w' },
1157 { "options", 1, NULL
, 'o' },
1158 { "type", 1, NULL
, 't' },
1159 { "rsize",1, NULL
, 'R' },
1160 { "wsize",1, NULL
, 'W' },
1161 { "uid", 1, NULL
, '1'},
1162 { "gid", 1, NULL
, '2'},
1163 { "user",1,NULL
,'u'},
1164 { "username",1,NULL
,'u'},
1165 { "dom",1,NULL
,'d'},
1166 { "domain",1,NULL
,'d'},
1167 { "password",1,NULL
,'p'},
1168 { "pass",1,NULL
,'p'},
1169 { "credentials",1,NULL
,'c'},
1170 { "port",1,NULL
,'P'},
1171 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1172 { NULL
, 0, NULL
, 0 }
1175 /* convert a string to uppercase. return false if the string
1176 * wasn't ASCII. Return success on a NULL ptr */
1178 uppercase_string(char *string
)
1184 /* check for unicode */
1185 if ((unsigned char) string
[0] & 0x80)
1187 *string
= toupper((unsigned char) *string
);
1194 static void print_cifs_mount_version(void)
1196 printf("mount.cifs version: %s.%s%s\n",
1197 MOUNT_CIFS_VERSION_MAJOR
,
1198 MOUNT_CIFS_VERSION_MINOR
,
1199 MOUNT_CIFS_VENDOR_SUFFIX
);
1203 * This function borrowed from fuse-utils...
1205 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1206 * newlines embedded within the text fields. To make sure no one corrupts
1207 * the mtab, fail the mount if there are embedded newlines.
1209 static int check_newline(const char *progname
, const char *name
)
1212 for (s
= "\n"; *s
; s
++) {
1213 if (strchr(name
, *s
)) {
1214 fprintf(stderr
, "%s: illegal character 0x%02x in mount entry\n",
1222 static int check_mtab(const char *progname
, const char *devname
,
1225 if (check_newline(progname
, devname
) == -1 ||
1226 check_newline(progname
, dir
) == -1)
1232 int main(int argc
, char ** argv
)
1235 unsigned long flags
= MS_MANDLOCK
;
1236 char * orgoptions
= NULL
;
1237 char * share_name
= NULL
;
1238 const char * ipaddr
= NULL
;
1240 char * mountpoint
= NULL
;
1241 char * options
= NULL
;
1243 char * resolved_path
= NULL
;
1254 size_t options_size
= 0;
1256 int retry
= 0; /* set when we have to retry mount with uppercase */
1257 struct addrinfo
*addrhead
= NULL
, *addr
;
1258 struct utsname sysinfo
;
1259 struct mntent mountent
;
1260 struct sockaddr_in
*addr4
;
1261 struct sockaddr_in6
*addr6
;
1267 /* setlocale(LC_ALL, "");
1268 bindtextdomain(PACKAGE, LOCALEDIR);
1269 textdomain(PACKAGE); */
1272 thisprogram
= argv
[0];
1274 mount_cifs_usage(stderr
);
1276 if(thisprogram
== NULL
)
1277 thisprogram
= "mount.cifs";
1280 /* BB add workstation name and domain and pass down */
1282 /* #ifdef _GNU_SOURCE
1283 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1287 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1288 if (share_name
== NULL
) {
1289 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1292 mountpoint
= argv
[2];
1293 } else if (argc
== 2) {
1294 if ((strcmp(argv
[1], "-V") == 0) ||
1295 (strcmp(argv
[1], "--version") == 0))
1297 print_cifs_mount_version();
1301 if ((strcmp(argv
[1], "-h") == 0) ||
1302 (strcmp(argv
[1], "-?") == 0) ||
1303 (strcmp(argv
[1], "--help") == 0))
1304 mount_cifs_usage(stdout
);
1306 mount_cifs_usage(stderr
);
1308 mount_cifs_usage(stderr
);
1312 /* add sharename in opts string as unc= parm */
1313 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1314 longopts
, NULL
)) != -1) {
1316 /* No code to do the following options yet */
1318 list_with_volumelabel = 1;
1321 volumelabel = optarg;
1328 case 'h': /* help */
1329 mount_cifs_usage(stdout
);
1338 "option 'b' (MS_BIND) not supported\n");
1346 "option 'm' (MS_MOVE) not supported\n");
1350 orgoptions
= strdup(optarg
);
1352 case 'r': /* mount readonly */
1362 print_cifs_mount_version();
1365 flags
&= ~MS_RDONLY
;
1368 rsize
= atoi(optarg
) ;
1371 wsize
= atoi(optarg
);
1374 if (isdigit(*optarg
)) {
1377 uid
= strtoul(optarg
, &ep
, 10);
1379 fprintf(stderr
, "bad uid value \"%s\"\n", optarg
);
1385 if (!(pw
= getpwnam(optarg
))) {
1386 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1394 if (isdigit(*optarg
)) {
1397 gid
= strtoul(optarg
, &ep
, 10);
1399 fprintf(stderr
, "bad gid value \"%s\"\n", optarg
);
1405 if (!(gr
= getgrnam(optarg
))) {
1406 fprintf(stderr
, "bad user name \"%s\"\n", optarg
);
1418 domain_name
= optarg
; /* BB fix this - currently ignored */
1422 if(mountpassword
== NULL
)
1423 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1426 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1430 get_password_from_file(0 /* stdin */,NULL
);
1438 fprintf(stderr
, "unknown mount option %c\n",c
);
1439 mount_cifs_usage(stderr
);
1443 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1444 mount_cifs_usage(stderr
);
1447 /* make sure mountpoint is legit */
1448 rc
= chdir(mountpoint
);
1450 fprintf(stderr
, "Couldn't chdir to %s: %s\n", mountpoint
,
1456 rc
= check_mountpoint(thisprogram
, mountpoint
);
1460 /* sanity check for unprivileged mounts */
1462 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1467 /* enable any default user mount flags */
1468 flags
|= CIFS_SETUID_FLAGS
;
1471 if (getenv("PASSWD")) {
1472 if(mountpassword
== NULL
)
1473 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1475 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1478 } else if (getenv("PASSWD_FD")) {
1479 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1480 } else if (getenv("PASSWD_FILE")) {
1481 get_password_from_file(0, getenv("PASSWD_FILE"));
1484 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1490 #if !CIFS_LEGACY_SETUID_CHECK
1491 if (!(flags
& (MS_USERS
|MS_USER
))) {
1492 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1496 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1499 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1500 "CIFS mounts not supported.",
1507 flags
&= ~(MS_USERS
|MS_USER
);
1509 addrhead
= addr
= parse_server(&share_name
);
1510 if((addrhead
== NULL
) && (got_ip
== 0)) {
1511 fprintf(stderr
, "No ip address specified and hostname not found\n");
1516 /* BB save off path and pop after mount returns? */
1517 resolved_path
= (char *)malloc(PATH_MAX
+1);
1518 if (!resolved_path
) {
1519 fprintf(stderr
, "Unable to allocate memory.\n");
1524 /* Note that if we can not canonicalize the name, we get
1525 another chance to see if it is valid when we chdir to it */
1526 if(!realpath(".", resolved_path
)) {
1527 fprintf(stderr
, "Unable to resolve %s to canonical path: %s\n",
1528 mountpoint
, strerror(errno
));
1533 mountpoint
= resolved_path
;
1536 /* Note that the password will not be retrieved from the
1537 USER env variable (ie user%password form) as there is
1538 already a PASSWD environment varaible */
1540 user_name
= strdup(getenv("USER"));
1541 if (user_name
== NULL
)
1542 user_name
= getusername();
1546 if(got_password
== 0) {
1547 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1548 no good replacement yet. */
1549 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1550 if (!tmp_pass
|| !mountpassword
) {
1551 fprintf(stderr
, "Password not entered, exiting\n");
1554 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1557 /* FIXME launch daemon (handles dfs name resolution and credential change)
1558 remember to clear parms and overwrite password field before launching */
1560 optlen
= strlen(orgoptions
);
1565 optlen
+= strlen(share_name
) + 4;
1567 fprintf(stderr
, "No server share name specified\n");
1568 fprintf(stderr
, "\nMounting the DFS root for server not implemented yet\n");
1572 optlen
+= strlen(user_name
) + 6;
1573 optlen
+= MAX_ADDRESS_LEN
+ 4;
1575 optlen
+= strlen(mountpassword
) + 6;
1578 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1579 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 */);
1581 if(options
== NULL
) {
1582 fprintf(stderr
, "Could not allocate memory for mount options\n");
1586 strlcpy(options
, "unc=", options_size
);
1587 strlcat(options
,share_name
,options_size
);
1588 /* scan backwards and reverse direction of slash */
1589 temp
= strrchr(options
, '/');
1590 if(temp
> options
+ 6)
1593 /* check for syntax like user=domain\user */
1595 domain_name
= check_for_domain(&user_name
);
1596 strlcat(options
,",user=",options_size
);
1597 strlcat(options
,user_name
,options_size
);
1601 /* extra length accounted for in option string above */
1602 strlcat(options
,",domain=",options_size
);
1603 strlcat(options
,domain_name
,options_size
);
1607 strlcat(options
,",ver=",options_size
);
1608 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1611 strlcat(options
,",",options_size
);
1612 strlcat(options
,orgoptions
,options_size
);
1615 strlcat(options
,",prefixpath=",options_size
);
1616 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1619 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1620 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1622 if (!got_ip
&& addr
) {
1623 strlcat(options
, ",ip=", options_size
);
1624 current_len
= strnlen(options
, options_size
);
1625 optionstail
= options
+ current_len
;
1626 switch (addr
->ai_addr
->sa_family
) {
1628 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1629 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1630 options_size
- current_len
);
1633 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1634 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1635 options_size
- current_len
);
1641 /* if the address looks bogus, try the next one */
1643 addr
= addr
->ai_next
;
1651 if (addr
&& addr
->ai_addr
->sa_family
== AF_INET6
&& addr6
->sin6_scope_id
) {
1652 strlcat(options
, "%", options_size
);
1653 current_len
= strnlen(options
, options_size
);
1654 optionstail
= options
+ current_len
;
1655 snprintf(optionstail
, options_size
- current_len
, "%u",
1656 addr6
->sin6_scope_id
);
1660 fprintf(stderr
, "\nmount.cifs kernel mount options: %s", options
);
1662 if (mountpassword
) {
1664 * Commas have to be doubled, or else they will
1665 * look like the parameter separator
1668 check_for_comma(&mountpassword
);
1669 strlcat(options
,",pass=",options_size
);
1670 strlcat(options
,mountpassword
,options_size
);
1672 fprintf(stderr
, ",pass=********");
1676 fprintf(stderr
, "\n");
1678 rc
= check_mtab(thisprogram
, dev_name
, mountpoint
);
1682 if (!fakemnt
&& mount(dev_name
, ".", "cifs", flags
, options
)) {
1687 addr
= addr
->ai_next
;
1693 fprintf(stderr
, "mount error: cifs filesystem not supported by the system\n");
1698 if (uppercase_string(dev_name
) &&
1699 uppercase_string(share_name
) &&
1700 uppercase_string(prefixpath
)) {
1701 fprintf(stderr
, "retrying with upper case share name\n");
1706 fprintf(stderr
, "mount error(%d): %s\n", errno
, strerror(errno
));
1707 fprintf(stderr
, "Refer to the mount.cifs(8) manual page (e.g. man "
1715 atexit(unlock_mtab
);
1718 fprintf(stderr
, "cannot lock mtab");
1721 pmntfile
= setmntent(MOUNTED
, "a+");
1723 fprintf(stderr
, "could not update mount table\n");
1728 mountent
.mnt_fsname
= dev_name
;
1729 mountent
.mnt_dir
= mountpoint
;
1730 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1731 mountent
.mnt_opts
= (char *)malloc(220);
1732 if(mountent
.mnt_opts
) {
1733 char * mount_user
= getusername();
1734 memset(mountent
.mnt_opts
,0,200);
1735 if(flags
& MS_RDONLY
)
1736 strlcat(mountent
.mnt_opts
,"ro",220);
1738 strlcat(mountent
.mnt_opts
,"rw",220);
1739 if(flags
& MS_MANDLOCK
)
1740 strlcat(mountent
.mnt_opts
,",mand",220);
1741 if(flags
& MS_NOEXEC
)
1742 strlcat(mountent
.mnt_opts
,",noexec",220);
1743 if(flags
& MS_NOSUID
)
1744 strlcat(mountent
.mnt_opts
,",nosuid",220);
1745 if(flags
& MS_NODEV
)
1746 strlcat(mountent
.mnt_opts
,",nodev",220);
1747 if(flags
& MS_SYNCHRONOUS
)
1748 strlcat(mountent
.mnt_opts
,",sync",220);
1751 strlcat(mountent
.mnt_opts
,
1753 strlcat(mountent
.mnt_opts
,
1758 mountent
.mnt_freq
= 0;
1759 mountent
.mnt_passno
= 0;
1760 rc
= addmntent(pmntfile
,&mountent
);
1761 endmntent(pmntfile
);
1763 SAFE_FREE(mountent
.mnt_opts
);
1768 int len
= strlen(mountpassword
);
1769 memset(mountpassword
,0,len
);
1770 SAFE_FREE(mountpassword
);
1774 freeaddrinfo(addrhead
);
1776 SAFE_FREE(orgoptions
);
1777 SAFE_FREE(resolved_path
);
1778 SAFE_FREE(share_name
);