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 <kzak@redhat.com>
This commit is contained in:
parent
916a3f8d29
commit
6497f2d99e
|
@ -40,6 +40,7 @@ mnt_context_enable_rwonly_mount
|
||||||
mnt_context_enable_sloppy
|
mnt_context_enable_sloppy
|
||||||
mnt_context_enable_verbose
|
mnt_context_enable_verbose
|
||||||
mnt_context_forced_rdonly
|
mnt_context_forced_rdonly
|
||||||
|
mnt_context_force_unrestricted
|
||||||
mnt_context_get_cache
|
mnt_context_get_cache
|
||||||
mnt_context_get_excode
|
mnt_context_get_excode
|
||||||
mnt_context_get_fs
|
mnt_context_get_fs
|
||||||
|
|
|
@ -426,6 +426,31 @@ int mnt_context_is_restricted(struct libmnt_context *cxt)
|
||||||
return cxt->restricted;
|
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
|
* mnt_context_set_optsmode
|
||||||
* @cxt: mount context
|
* @cxt: mount context
|
||||||
|
|
|
@ -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_reset_context(struct libmnt_context *cxt);
|
||||||
extern int mnt_context_is_restricted(struct libmnt_context *cxt)
|
extern int mnt_context_is_restricted(struct libmnt_context *cxt)
|
||||||
__ul_attribute__((nonnull));
|
__ul_attribute__((nonnull));
|
||||||
|
extern int mnt_context_force_unrestricted(struct libmnt_context *cxt);
|
||||||
|
|
||||||
extern int mnt_context_init_helper(struct libmnt_context *cxt,
|
extern int mnt_context_init_helper(struct libmnt_context *cxt,
|
||||||
int action, int flags);
|
int action, int flags);
|
||||||
|
|
|
@ -352,6 +352,7 @@ MOUNT_2.34 {
|
||||||
} MOUNT_2.33;
|
} MOUNT_2.33;
|
||||||
|
|
||||||
MOUNT_2_35 {
|
MOUNT_2_35 {
|
||||||
|
mnt_context_force_unrestricted;
|
||||||
mnt_context_get_target_prefix;
|
mnt_context_get_target_prefix;
|
||||||
mnt_context_set_target_prefix;
|
mnt_context_set_target_prefix;
|
||||||
} MOUNT_2.34;
|
} MOUNT_2.34;
|
||||||
|
|
|
@ -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
|
specify filesystem, otherwise \fBmount\fR may fail. For example it's bad idea
|
||||||
to use NFS or CIFS source on command line.
|
to use NFS or CIFS source on command line.
|
||||||
.PP
|
.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
|
For more details, see
|
||||||
.BR fstab (5).
|
.BR fstab (5).
|
||||||
Only the user that mounted a filesystem can unmount it again.
|
Only the user that mounted a filesystem can unmount it again.
|
||||||
|
|
|
@ -47,23 +47,24 @@
|
||||||
|
|
||||||
static int mk_exit_code(struct libmnt_context *cxt, int rc);
|
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 ruid = getuid();
|
||||||
const uid_t euid = geteuid();
|
const uid_t euid = geteuid();
|
||||||
|
|
||||||
if (ruid == 0 && euid != 0) {
|
if (ruid != 0 && euid == 0) {
|
||||||
/* user is root, but setuid to non-root */
|
if (setgid(getgid()) < 0)
|
||||||
if (option)
|
err(MNT_EX_FAIL, _("setgid() failed"));
|
||||||
errx(MNT_EX_USAGE, _("only root can use \"--%s\" option "
|
|
||||||
"(effective UID is %u)"),
|
if (setuid(getuid()) < 0)
|
||||||
option, euid);
|
err(MNT_EX_FAIL, _("setuid() failed"));
|
||||||
errx(MNT_EX_USAGE, _("only root can do that "
|
|
||||||
"(effective UID is %u)"), euid);
|
|
||||||
}
|
}
|
||||||
if (option)
|
|
||||||
errx(MNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
|
/* be paranoid and check it, setuid(0) has to fail */
|
||||||
errx(MNT_EX_USAGE, _("only root can do that"));
|
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)
|
static void __attribute__((__noreturn__)) mount_print_version(void)
|
||||||
|
@ -672,7 +673,7 @@ int main(int argc, char **argv)
|
||||||
!strchr("hlLUVvrist", c) &&
|
!strchr("hlLUVvrist", c) &&
|
||||||
c != MOUNT_OPT_TARGET &&
|
c != MOUNT_OPT_TARGET &&
|
||||||
c != MOUNT_OPT_SOURCE)
|
c != MOUNT_OPT_SOURCE)
|
||||||
exit_non_root(option_to_longopt(c, longopts));
|
suid_drop(cxt);
|
||||||
|
|
||||||
err_exclusive_options(c, longopts, excl, excl_st);
|
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(),
|
/* Non-root users are allowed to use -t to print_all(),
|
||||||
but not to mount */
|
but not to mount */
|
||||||
if (mnt_context_is_restricted(cxt) && types)
|
if (mnt_context_is_restricted(cxt) && types)
|
||||||
exit_non_root("types");
|
suid_drop(cxt);
|
||||||
|
|
||||||
if (oper && (types || all || mnt_context_get_source(cxt))) {
|
if (oper && (types || all || mnt_context_get_source(cxt))) {
|
||||||
warnx(_("bad usage"));
|
warnx(_("bad usage"));
|
||||||
|
@ -905,7 +906,7 @@ int main(int argc, char **argv)
|
||||||
if (mnt_context_is_restricted(cxt) &&
|
if (mnt_context_is_restricted(cxt) &&
|
||||||
mnt_context_get_source(cxt) &&
|
mnt_context_get_source(cxt) &&
|
||||||
mnt_context_get_target(cxt))
|
mnt_context_get_target(cxt))
|
||||||
exit_non_root(NULL);
|
suid_drop(cxt);
|
||||||
|
|
||||||
} else if (argc == 1 && (!mnt_context_get_source(cxt) ||
|
} else if (argc == 1 && (!mnt_context_get_source(cxt) ||
|
||||||
!mnt_context_get_target(cxt))) {
|
!mnt_context_get_target(cxt))) {
|
||||||
|
@ -933,7 +934,7 @@ int main(int argc, char **argv)
|
||||||
if (mnt_context_is_restricted(cxt) &&
|
if (mnt_context_is_restricted(cxt) &&
|
||||||
mnt_context_get_source(cxt) &&
|
mnt_context_get_source(cxt) &&
|
||||||
mnt_context_get_target(cxt))
|
mnt_context_get_target(cxt))
|
||||||
exit_non_root(NULL);
|
suid_drop(cxt);
|
||||||
|
|
||||||
} else if (argc == 2 && !mnt_context_get_source(cxt)
|
} else if (argc == 2 && !mnt_context_get_source(cxt)
|
||||||
&& !mnt_context_get_target(cxt)) {
|
&& !mnt_context_get_target(cxt)) {
|
||||||
|
@ -941,7 +942,7 @@ int main(int argc, char **argv)
|
||||||
* D) mount <source> <target>
|
* D) mount <source> <target>
|
||||||
*/
|
*/
|
||||||
if (mnt_context_is_restricted(cxt))
|
if (mnt_context_is_restricted(cxt))
|
||||||
exit_non_root(NULL);
|
suid_drop(cxt);
|
||||||
|
|
||||||
mnt_context_set_source(cxt, argv[0]);
|
mnt_context_set_source(cxt, argv[0]);
|
||||||
mnt_context_set_target(cxt, argv[1]);
|
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);
|
mnt_context_set_optsmode(cxt, MNT_OMODE_NOTAB);
|
||||||
|
|
||||||
rc = mnt_context_mount(cxt);
|
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);
|
rc = mk_exit_code(cxt, rc);
|
||||||
|
|
||||||
if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
|
if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
|
||||||
|
|
|
@ -190,6 +190,25 @@ Display version information and exit.
|
||||||
.TP
|
.TP
|
||||||
.BR \-h , " \-\-help"
|
.BR \-h , " \-\-help"
|
||||||
Display help text and exit.
|
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"
|
.SH "LOOP DEVICE"
|
||||||
The
|
The
|
||||||
.B umount
|
.B umount
|
||||||
|
|
|
@ -112,24 +112,24 @@ static void __attribute__((__noreturn__)) usage(void)
|
||||||
exit(MNT_EX_SUCCESS);
|
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 ruid = getuid();
|
||||||
const uid_t euid = geteuid();
|
const uid_t euid = geteuid();
|
||||||
|
|
||||||
if (ruid == 0 && euid != 0) {
|
if (ruid != 0 && euid == 0) {
|
||||||
/* user is root, but setuid to non-root */
|
if (setgid(getgid()) < 0)
|
||||||
if (option)
|
err(MNT_EX_FAIL, _("setgid() failed"));
|
||||||
errx(MNT_EX_USAGE,
|
|
||||||
_("only root can use \"--%s\" option "
|
if (setuid(getuid()) < 0)
|
||||||
"(effective UID is %u)"),
|
err(MNT_EX_FAIL, _("setuid() failed"));
|
||||||
option, euid);
|
|
||||||
errx(MNT_EX_USAGE, _("only root can do that "
|
|
||||||
"(effective UID is %u)"), euid);
|
|
||||||
}
|
}
|
||||||
if (option)
|
|
||||||
errx(MNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
|
/* be paranoid and check it, setuid(0) has to fail */
|
||||||
errx(MNT_EX_USAGE, _("only root can do that"));
|
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)
|
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"));
|
err(MNT_EX_SYSERR, _("failed to set umount target"));
|
||||||
|
|
||||||
rc = mnt_context_umount(cxt);
|
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);
|
rc = mk_exit_code(cxt, rc);
|
||||||
|
|
||||||
if (rc == MNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
|
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 */
|
/* only few options are allowed for non-root users */
|
||||||
if (mnt_context_is_restricted(cxt) && !strchr("hdilqVv", c))
|
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);
|
err_exclusive_options(c, longopts, excl, excl_st);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue