From: Karel Zak Date: Tue, 19 Nov 2019 13:58:20 +0000 (+0100) Subject: mount: no exit on EPERM, continue without suid X-Git-Tag: v2.35-rc1~31^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6497f2d99e9cabee3531e644ba4dcffd14532200;p=thirdparty%2Futil-linux.git mount: no exit on EPERM, continue without suid 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 --- diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt index 990c0394f2..0bba9f8356 100644 --- a/libmount/docs/libmount-sections.txt +++ b/libmount/docs/libmount-sections.txt @@ -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 diff --git a/libmount/src/context.c b/libmount/src/context.c index 977842f7d8..72a0c5a7b0 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -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 diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 19d4c5b53c..ba54cf25da 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index b9a4c0d22d..792d11753e 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -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; diff --git a/sys-utils/mount.8 b/sys-utils/mount.8 index 9d31d8245b..a6231c7c1c 100644 --- a/sys-utils/mount.8 +++ b/sys-utils/mount.8 @@ -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. diff --git a/sys-utils/mount.c b/sys-utils/mount.c index 08da9e6a54..5842bc2ecc 100644 --- a/sys-utils/mount.c +++ b/sys-utils/mount.c @@ -47,23 +47,24 @@ 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 */ 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)) diff --git a/sys-utils/umount.8 b/sys-utils/umount.8 index f94d2f41f4..9bec521e7f 100644 --- a/sys-utils/umount.8 +++ b/sys-utils/umount.8 @@ -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 diff --git a/sys-utils/umount.c b/sys-utils/umount.c index 397e0ebfc5..74d87d671b 100644 --- a/sys-utils/umount.c +++ b/sys-utils/umount.c @@ -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);