summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--mount.cifs.83
-rw-r--r--mount.cifs.c85
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;