]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
mount: no exit on EPERM, continue without suid
authorKarel Zak <kzak@redhat.com>
Tue, 19 Nov 2019 13:58:20 +0000 (14:58 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 19 Nov 2019 13:58:20 +0000 (14:58 +0100)
The current libmount assumes that mount(8) and umount(8) are suid
binaries. For this reason it implements internal rules which
restrict what is allowed for non-root users. Unfortunately, it's
out of reality for some use-cases where root permissions are no
required. Nice example are fuse filesystems.

So, the current situation is to call exit() always when mount, umount or
libmount are unsure with non-root user rights. This patch removes the
exit() call and replaces it with suid permissions drop, after that it
continues as usually. It means after suid-drop all depend on kernel
and no another security rule is used by libmount (simply because any
rule is no more necessary).

Example:

old version:
   $ mount -t fuse.sshfs kzak@192.168.111.1:/home/kzak /home/kzak/mnt
   mount: only root can use "--types" option

new version:
   $ mount -t fuse.sshfs kzak@192.168.111.1:/home/kzak /home/kzak/mnt
   kzak@192.168.111.1's password:

   $ findmnt /home/kzak/mnt
   TARGET         SOURCE                        FSTYPE     OPTIONS
   /home/kzak/mnt kzak@192.168.111.1:/home/kzak fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000

   $ umount /home/kzak/mnt
   $ echo $?
   0

Note that fuse user umount is supported since v2.34 due to user_id= in
kernel mount table.

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/docs/libmount-sections.txt
libmount/src/context.c
libmount/src/libmount.h.in
libmount/src/libmount.sym
sys-utils/mount.8
sys-utils/mount.c
sys-utils/umount.8
sys-utils/umount.c

index 990c0394f2b71adbb674141da02303754dd20070..0bba9f8356e7bb8edd28a8547270dc13f01144ee 100644 (file)
@@ -40,6 +40,7 @@ mnt_context_enable_rwonly_mount
 mnt_context_enable_sloppy
 mnt_context_enable_verbose
 mnt_context_forced_rdonly
+mnt_context_force_unrestricted
 mnt_context_get_cache
 mnt_context_get_excode
 mnt_context_get_fs
index 977842f7d89af6fd1e406caed503ba58cd12eb2f..72a0c5a7b0d0ce646a449f3f725ab5a90786c86c 100644 (file)
@@ -426,6 +426,31 @@ int mnt_context_is_restricted(struct libmnt_context *cxt)
        return cxt->restricted;
 }
 
+/**
+ * mnt_context_force_unrestricted:
+ * @cxt: mount context
+ *
+ * This function is DANGEROURS as it disables all security policies in libmount.
+ * Don't use if not sure. It removes "restricted" flag from the context, so
+ * libmount will use the current context as for root user.
+ *
+ * This function is designed for case you have no any suid permissions, so you
+ * can depend on kernel.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ *
+ * Since: 2.35
+ */
+int mnt_context_force_unrestricted(struct libmnt_context *cxt)
+{
+       if (mnt_context_is_restricted(cxt)) {
+               DBG(CXT, ul_debugobj(cxt, "force UNRESTRICTED"));
+               cxt->restricted = 0;
+       }
+
+       return 0;
+}
+
 /**
  * mnt_context_set_optsmode
  * @cxt: mount context
index 19d4c5b53cd1fb5bdfe4ac0981db9e88828218ee..ba54cf25da9d59923d0f14b49d712ecc20c6407a 100644 (file)
@@ -705,6 +705,7 @@ extern void mnt_free_context(struct libmnt_context *cxt);
 extern int mnt_reset_context(struct libmnt_context *cxt);
 extern int mnt_context_is_restricted(struct libmnt_context *cxt)
                        __ul_attribute__((nonnull));
+extern int mnt_context_force_unrestricted(struct libmnt_context *cxt);
 
 extern int mnt_context_init_helper(struct libmnt_context *cxt,
                                   int action, int flags);
index b9a4c0d22dff4361f56c67be1db332aeb8e6c280..792d11753e24bb2bb367bd76d5555652f8a4b928 100644 (file)
@@ -352,6 +352,7 @@ MOUNT_2.34 {
 } MOUNT_2.33;
 
 MOUNT_2_35 {
+       mnt_context_force_unrestricted;
        mnt_context_get_target_prefix;
        mnt_context_set_target_prefix;
 } MOUNT_2.34;
index 9d31d8245b3fe2e4758ebd9a59ff13e1f2e12649..a6231c7c1cfc6043ac294dc5c0dae23a6a82dd6d 100644 (file)
@@ -315,6 +315,12 @@ program is executed. It's strongly recommended to use a valid mountpoint to
 specify filesystem, otherwise \fBmount\fR may fail. For example it's bad idea
 to use NFS or CIFS source on command line.
 .PP
+Since version 2.35 \fBmount\fR command does not exit when user permissions are
+inadequate by internal libmount security rules.  It drops suid permissions
+and continue as regular non-root user. It allows to support use-cases where
+root permissions are not necessary (e.g. fuse filesystems, user namespaces,
+etc).
+.PP
 For more details, see
 .BR fstab (5).
 Only the user that mounted a filesystem can unmount it again.
index 08da9e6a546bb9dfd05fd9de3436280b3578f76a..5842bc2ecc88ffebd0e3a4abe46c66decc115cf5 100644 (file)
 
 static int mk_exit_code(struct libmnt_context *cxt, int rc);
 
-static void __attribute__((__noreturn__)) exit_non_root(const char *option)
+static void suid_drop(struct libmnt_context *cxt)
 {
        const uid_t ruid = getuid();
        const uid_t euid = geteuid();
 
-       if (ruid == 0 && euid != 0) {
-               /* user is root, but setuid to non-root */
-               if (option)
-                       errx(MNT_EX_USAGE, _("only root can use \"--%s\" option "
-                                        "(effective UID is %u)"),
-                                       option, euid);
-               errx(MNT_EX_USAGE, _("only root can do that "
-                                "(effective UID is %u)"), euid);
+       if (ruid != 0 && euid == 0) {
+               if (setgid(getgid()) < 0)
+                       err(MNT_EX_FAIL, _("setgid() failed"));
+
+               if (setuid(getuid()) < 0)
+                       err(MNT_EX_FAIL, _("setuid() failed"));
        }
-       if (option)
-               errx(MNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
-       errx(MNT_EX_USAGE, _("only root can do that"));
+
+       /* be paranoid and check it, setuid(0) has to fail */
+       if (ruid != 0 && setuid(0) == 0)
+               errx(MNT_EX_FAIL, _("drop permissions failed."));
+
+       mnt_context_force_unrestricted(cxt);
 }
 
 static void __attribute__((__noreturn__)) mount_print_version(void)
@@ -672,7 +673,7 @@ int main(int argc, char **argv)
                    !strchr("hlLUVvrist", c) &&
                    c != MOUNT_OPT_TARGET &&
                    c != MOUNT_OPT_SOURCE)
-                       exit_non_root(option_to_longopt(c, longopts));
+                       suid_drop(cxt);
 
                err_exclusive_options(c, longopts, excl, excl_st);
 
@@ -872,7 +873,7 @@ int main(int argc, char **argv)
        /* Non-root users are allowed to use -t to print_all(),
           but not to mount */
        if (mnt_context_is_restricted(cxt) && types)
-               exit_non_root("types");
+               suid_drop(cxt);
 
        if (oper && (types || all || mnt_context_get_source(cxt))) {
                warnx(_("bad usage"));
@@ -905,7 +906,7 @@ int main(int argc, char **argv)
                if (mnt_context_is_restricted(cxt) &&
                    mnt_context_get_source(cxt) &&
                    mnt_context_get_target(cxt))
-                       exit_non_root(NULL);
+                       suid_drop(cxt);
 
        } else if (argc == 1 && (!mnt_context_get_source(cxt) ||
                                 !mnt_context_get_target(cxt))) {
@@ -933,7 +934,7 @@ int main(int argc, char **argv)
                if (mnt_context_is_restricted(cxt) &&
                    mnt_context_get_source(cxt) &&
                    mnt_context_get_target(cxt))
-                       exit_non_root(NULL);
+                       suid_drop(cxt);
 
        } else if (argc == 2 && !mnt_context_get_source(cxt)
                             && !mnt_context_get_target(cxt)) {
@@ -941,7 +942,7 @@ int main(int argc, char **argv)
                 * D) mount <source> <target>
                 */
                if (mnt_context_is_restricted(cxt))
-                       exit_non_root(NULL);
+                       suid_drop(cxt);
 
                mnt_context_set_source(cxt, argv[0]);
                mnt_context_set_target(cxt, argv[1]);
@@ -963,6 +964,14 @@ int main(int argc, char **argv)
                mnt_context_set_optsmode(cxt, MNT_OMODE_NOTAB);
 
        rc = mnt_context_mount(cxt);
+
+       if (rc == -EPERM
+           && mnt_context_is_restricted(cxt)
+           && !mnt_context_syscall_called(cxt)) {
+               /* Try it again without permissions */
+               suid_drop(cxt);
+               rc = mnt_context_mount(cxt);
+       }
        rc = mk_exit_code(cxt, rc);
 
        if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
index f94d2f41f4b6e4f48b01b8e0551798763a10fa94..9bec521e7f0a1852f2823dd3b9ef805f1c24521d 100644 (file)
@@ -190,6 +190,25 @@ Display version information and exit.
 .TP
 .BR \-h , " \-\-help"
 Display help text and exit.
+.SH "NON-SUPERUSER UMOUNTS"
+Normally, only the superuser can umount filesystems.
+However, when
+.I fstab
+contains the
+.B user
+option on a line, anybody can umount the corresponding filesystem.  For more details see
+.BR mount (8)
+man page.
+.PP
+Since version 2.34 \fBumount\fR command allows to perform umount operation also
+for fuse filesystems if kernel mount table contains user's ID.  In this case fstab
+user= mount option is not required.
+.PP
+Since version 2.35 \fBumount\fR command does not exit when user permissions are
+inadequate by internal libmount security rules.  It drops suid permissions
+and continue as regular non-root user. It allows to support use-cases where
+root permissions are not necessary (e.g. fuse filesystems, user namespaces,
+etc).
 .SH "LOOP DEVICE"
 The
 .B umount
index 397e0ebfc5df554b9471969a7fab967d2b29548c..74d87d671b726a37d9fd2e15739b17c3e4d90809 100644 (file)
@@ -112,24 +112,24 @@ static void __attribute__((__noreturn__)) usage(void)
        exit(MNT_EX_SUCCESS);
 }
 
-static void __attribute__((__noreturn__)) exit_non_root(const char *option)
+static void suid_drop(struct libmnt_context *cxt)
 {
        const uid_t ruid = getuid();
        const uid_t euid = geteuid();
 
-       if (ruid == 0 && euid != 0) {
-               /* user is root, but setuid to non-root */
-               if (option)
-                       errx(MNT_EX_USAGE,
-                               _("only root can use \"--%s\" option "
-                                "(effective UID is %u)"),
-                                       option, euid);
-               errx(MNT_EX_USAGE, _("only root can do that "
-                                "(effective UID is %u)"), euid);
+       if (ruid != 0 && euid == 0) {
+               if (setgid(getgid()) < 0)
+                       err(MNT_EX_FAIL, _("setgid() failed"));
+
+               if (setuid(getuid()) < 0)
+                       err(MNT_EX_FAIL, _("setuid() failed"));
        }
-       if (option)
-               errx(MNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
-       errx(MNT_EX_USAGE, _("only root can do that"));
+
+       /* be paranoid and check it, setuid(0) has to fail */
+       if (ruid != 0 && setuid(0) == 0)
+               errx(MNT_EX_FAIL, _("drop permissions failed."));
+
+       mnt_context_force_unrestricted(cxt);
 }
 
 static void success_message(struct libmnt_context *cxt)
@@ -220,6 +220,15 @@ static int umount_one(struct libmnt_context *cxt, const char *spec)
                err(MNT_EX_SYSERR, _("failed to set umount target"));
 
        rc = mnt_context_umount(cxt);
+
+       if (rc == -EPERM
+           && mnt_context_is_restricted(cxt)
+           && !mnt_context_syscall_called(cxt)) {
+               /* Failed somewhere in libmount, drop perms and try it again */
+               suid_drop(cxt);
+               rc = mnt_context_umount(cxt);
+       }
+
        rc = mk_exit_code(cxt, rc);
 
        if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
@@ -494,7 +503,7 @@ int main(int argc, char **argv)
 
                /* only few options are allowed for non-root users */
                if (mnt_context_is_restricted(cxt) && !strchr("hdilqVv", c))
-                       exit_non_root(option_to_longopt(c, longopts));
+                       suid_drop(cxt);
 
                err_exclusive_options(c, longopts, excl, excl_st);