attr: add in_group_or_capable()

commit 11c2a8700cdcabf9b639b7204a1e38e2a0b6798e upstream.

[backport to 5.15.y, prior to vfsgid_t]

In setattr_{copy,prepare}() we need to perform the same permission
checks to determine whether we need to drop the setgid bit or not.
Instead of open-coding it twice add a simple helper the encapsulates the
logic. We will reuse this helpers to make dropping the setgid bit during
write operations more consistent in a follow up patch.

Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Tested-by: Leah Rumancik <leah.rumancik@gmail.com>
Acked-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Christian Brauner 2023-03-07 10:59:18 -08:00 committed by Greg Kroah-Hartman
parent 0123712492
commit 93395e1184
3 changed files with 30 additions and 8 deletions

View File

@ -18,6 +18,8 @@
#include <linux/evm.h>
#include <linux/ima.h>
#include "internal.h"
/**
* chown_ok - verify permissions to chown inode
* @mnt_userns: user namespace of the mount @inode was found from
@ -141,8 +143,7 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry,
mapped_gid = i_gid_into_mnt(mnt_userns, inode);
/* Also check the setgid bit! */
if (!in_group_p(mapped_gid) &&
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
if (!in_group_or_capable(mnt_userns, inode, mapped_gid))
attr->ia_mode &= ~S_ISGID;
}
@ -257,8 +258,7 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
if (!in_group_p(kgid) &&
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
if (!in_group_or_capable(mnt_userns, inode, kgid))
mode &= ~S_ISGID;
inode->i_mode = mode;
}

View File

@ -2321,6 +2321,28 @@ struct timespec64 current_time(struct inode *inode)
}
EXPORT_SYMBOL(current_time);
/**
* in_group_or_capable - check whether caller is CAP_FSETID privileged
* @mnt_userns: user namespace of the mount @inode was found from
* @inode: inode to check
* @gid: the new/current gid of @inode
*
* Check wether @gid is in the caller's group list or if the caller is
* privileged with CAP_FSETID over @inode. This can be used to determine
* whether the setgid bit can be kept or must be dropped.
*
* Return: true if the caller is sufficiently privileged, false if not.
*/
bool in_group_or_capable(struct user_namespace *mnt_userns,
const struct inode *inode, kgid_t gid)
{
if (in_group_p(gid))
return true;
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
return true;
return false;
}
/**
* mode_strip_sgid - handle the sgid bit for non-directories
* @mnt_userns: User namespace of the mount the inode was created from
@ -2342,11 +2364,9 @@ umode_t mode_strip_sgid(struct user_namespace *mnt_userns,
return mode;
if (S_ISDIR(mode) || !dir || !(dir->i_mode & S_ISGID))
return mode;
if (in_group_p(i_gid_into_mnt(mnt_userns, dir)))
if (in_group_or_capable(mnt_userns, dir,
i_gid_into_mnt(mnt_userns, dir)))
return mode;
if (capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID))
return mode;
return mode & ~S_ISGID;
}
EXPORT_SYMBOL(mode_strip_sgid);

View File

@ -150,6 +150,8 @@ extern int vfs_open(const struct path *, struct file *);
extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc);
extern void inode_add_lru(struct inode *inode);
extern int dentry_needs_remove_privs(struct dentry *dentry);
bool in_group_or_capable(struct user_namespace *mnt_userns,
const struct inode *inode, kgid_t gid);
/*
* fs-writeback.c