diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | mount.cifs.8 | 3 | ||||
-rw-r--r-- | mount.cifs.c | 85 |
3 files changed, 72 insertions, 20 deletions
diff --git a/configure.ac b/configure.ac index 3027eba..1f561f5 100644 --- a/configure.ac +++ b/configure.ac @@ -77,6 +77,10 @@ AC_CHECK_FUNCS(clock_gettime, [], [ # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h ctype.h fcntl.h inttypes.h limits.h mntent.h netdb.h stddef.h stdint.h stdbool.h stdlib.h stdio.h errno.h string.h strings.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h], , [AC_MSG_ERROR([necessary header(s) not found])]) +# do we have sys/fsuid.h and setfsuid()? +AC_CHECK_HEADERS([sys/fsuid.h]) +AC_CHECK_FUNC(setfsuid, , [AC_MSG_ERROR([System does not support setfsuid()])]) + if test $enable_cifsupcall != "no"; then AC_CHECK_HEADERS([krb5.h krb5/krb5.h]) if test x$ac_cv_header_krb5_krb5_h != xyes ; then diff --git a/mount.cifs.8 b/mount.cifs.8 index 1f07d2c..cbf2e76 100644 --- a/mount.cifs.8 +++ b/mount.cifs.8 @@ -659,7 +659,8 @@ The variable may contain the pathname of a file to read the password from\&. A single line of input is read and used as the password\&. .SH "NOTES" .PP -This command may be used only by root, unless installed setuid, in which case the noeexec and nosuid mount flags are enabled\&. When installed as a setuid program, the program follows the conventions set forth by the mount program for user mounts\&. +This command may be used only by root, unless installed setuid, in which case the noeexec and nosuid mount flags are enabled\&. When installed as a setuid program, the program follows the conventions set forth by the mount program for user mounts, with the added restriction that users must be able to chdir() into the +mountpoint prior to the mount in order to be able to mount onto it. .PP Some samba client tools like smbclient(8) honour client\-side configuration parameters present in smb\&.conf\&. Unlike those client tools, \fImount\&.cifs\fR diff --git a/mount.cifs.c b/mount.cifs.c index c0aea35..f0b073e 100644 --- a/mount.cifs.c +++ b/mount.cifs.c @@ -45,6 +45,9 @@ #include <libgen.h> #include <sys/mman.h> #include <sys/wait.h> +#ifdef HAVE_SYS_FSUID_H +#include <sys/fsuid.h> +#endif /* HAVE_SYS_FSUID_H */ #ifdef HAVE_LIBCAP_NG #include <cap-ng.h> #else /* HAVE_LIBCAP_NG */ @@ -1854,6 +1857,68 @@ assemble_exit: return rc; } +/* + * chdir() into the mountpoint and determine "realpath". We assume here that + * "mountpoint" is a statically allocated string and does not need to be freed. + */ +static int +acquire_mountpoint(char **mountpointp) +{ + int rc, dacrc; + uid_t realuid, oldfsuid; + gid_t oldfsgid; + char *mountpoint; + + /* + * Acquire the necessary privileges to chdir to the mountpoint. If + * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If + * it's not, then we change the fsuid to the real uid to ensure that + * the mounting user actually has access to the mountpoint. + * + * The mount(8) manpage does not state that users must be able to + * chdir into the mountpoint in order to mount onto it, but if we + * allow that, then an unprivileged user could use this program to + * "probe" into directories to which he does not have access. + */ + realuid = getuid(); + if (realuid == 0) { + dacrc = toggle_dac_capability(0, 1); + if (dacrc) + return dacrc; + } else { + oldfsuid = setfsuid(realuid); + oldfsgid = setfsgid(getgid()); + } + + rc = chdir(*mountpointp); + if (rc) { + fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp, + strerror(errno)); + rc = EX_USAGE; + goto restore_privs; + } + + mountpoint = realpath(".", NULL); + if (!mountpoint) { + fprintf(stderr, "Unable to resolve %s to canonical path: %s\n", + *mountpointp, strerror(errno)); + rc = EX_SYSERR; + } + + *mountpointp = mountpoint; +restore_privs: + if (realuid == 0) { + dacrc = toggle_dac_capability(0, 0); + if (dacrc) + rc = rc ? rc : dacrc; + } else { + setfsuid(oldfsuid); + setfsgid(oldfsgid); + } + + return rc; +} + int main(int argc, char **argv) { int c; @@ -1953,25 +2018,7 @@ int main(int argc, char **argv) mountpoint = argv[optind + 1]; /* chdir into mountpoint as soon as possible */ - rc = toggle_dac_capability(0, 1); - if (rc) - return rc; - rc = chdir(mountpoint); - if (rc) { - fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint, - strerror(errno)); - rc = EX_USAGE; - goto mount_exit; - } - - mountpoint = realpath(".", NULL); - if (!mountpoint) { - fprintf(stderr, "Unable to resolve %s to canonical path: %s\n", - mountpoint, strerror(errno)); - rc = EX_SYSERR; - goto mount_exit; - } - rc = toggle_dac_capability(0, 0); + rc = acquire_mountpoint(&mountpoint); if (rc) return rc; |