Merge bdfae5ce38 ("Merge tag 'fs.idmapped.vfsuid.v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux") into android-mainline

Steps on the way to 6.0-rc1

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I8086188e24a5e25b8b6b289353823365786d6fe2
This commit is contained in:
Greg Kroah-Hartman
2022-08-03 15:41:12 +02:00
22 changed files with 546 additions and 175 deletions

View File

@@ -22,7 +22,7 @@
* chown_ok - verify permissions to chown inode
* @mnt_userns: user namespace of the mount @inode was found from
* @inode: inode to check permissions on
* @uid: uid to chown @inode to
* @ia_vfsuid: uid to chown @inode to
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then
@@ -31,15 +31,15 @@
* performed on the raw inode simply passs init_user_ns.
*/
static bool chown_ok(struct user_namespace *mnt_userns,
const struct inode *inode,
kuid_t uid)
const struct inode *inode, vfsuid_t ia_vfsuid)
{
kuid_t kuid = i_uid_into_mnt(mnt_userns, inode);
if (uid_eq(current_fsuid(), kuid) && uid_eq(uid, inode->i_uid))
vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
if (vfsuid_eq_kuid(vfsuid, current_fsuid()) &&
vfsuid_eq(ia_vfsuid, vfsuid))
return true;
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
return true;
if (uid_eq(kuid, INVALID_UID) &&
if (!vfsuid_valid(vfsuid) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
return true;
return false;
@@ -49,7 +49,7 @@ static bool chown_ok(struct user_namespace *mnt_userns,
* chgrp_ok - verify permissions to chgrp inode
* @mnt_userns: user namespace of the mount @inode was found from
* @inode: inode to check permissions on
* @gid: gid to chown @inode to
* @ia_vfsgid: gid to chown @inode to
*
* If the inode has been found through an idmapped mount the user namespace of
* the vfsmount must be passed through @mnt_userns. This function will then
@@ -58,21 +58,19 @@ static bool chown_ok(struct user_namespace *mnt_userns,
* performed on the raw inode simply passs init_user_ns.
*/
static bool chgrp_ok(struct user_namespace *mnt_userns,
const struct inode *inode, kgid_t gid)
const struct inode *inode, vfsgid_t ia_vfsgid)
{
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
if (uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode))) {
kgid_t mapped_gid;
if (gid_eq(gid, inode->i_gid))
vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
if (vfsuid_eq_kuid(vfsuid, current_fsuid())) {
if (vfsgid_eq(ia_vfsgid, vfsgid))
return true;
mapped_gid = mapped_kgid_fs(mnt_userns, i_user_ns(inode), gid);
if (in_group_p(mapped_gid))
if (vfsgid_in_group_p(ia_vfsgid))
return true;
}
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
return true;
if (gid_eq(kgid, INVALID_GID) &&
if (!vfsgid_valid(vfsgid) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
return true;
return false;
@@ -120,28 +118,29 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry,
goto kill_priv;
/* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) && !chown_ok(mnt_userns, inode, attr->ia_uid))
if ((ia_valid & ATTR_UID) &&
!chown_ok(mnt_userns, inode, attr->ia_vfsuid))
return -EPERM;
/* Make sure caller can chgrp. */
if ((ia_valid & ATTR_GID) && !chgrp_ok(mnt_userns, inode, attr->ia_gid))
if ((ia_valid & ATTR_GID) &&
!chgrp_ok(mnt_userns, inode, attr->ia_vfsgid))
return -EPERM;
/* Make sure a caller can chmod. */
if (ia_valid & ATTR_MODE) {
kgid_t mapped_gid;
vfsgid_t vfsgid;
if (!inode_owner_or_capable(mnt_userns, inode))
return -EPERM;
if (ia_valid & ATTR_GID)
mapped_gid = mapped_kgid_fs(mnt_userns,
i_user_ns(inode), attr->ia_gid);
vfsgid = attr->ia_vfsgid;
else
mapped_gid = i_gid_into_mnt(mnt_userns, inode);
vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
/* Also check the setgid bit! */
if (!in_group_p(mapped_gid) &&
if (!vfsgid_in_group_p(vfsgid) &&
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
attr->ia_mode &= ~S_ISGID;
}
@@ -219,9 +218,7 @@ EXPORT_SYMBOL(inode_newsize_ok);
* setattr_copy must be called with i_mutex held.
*
* setattr_copy updates the inode's metadata with that specified
* in attr on idmapped mounts. If file ownership is changed setattr_copy
* doesn't map ia_uid and ia_gid. It will asssume the caller has already
* provided the intended values. Necessary permission checks to determine
* in attr on idmapped mounts. Necessary permission checks to determine
* whether or not the S_ISGID property needs to be removed are performed with
* the correct idmapped mount permission helpers.
* Noticeably missing is inode size update, which is more complex
@@ -242,10 +239,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
{
unsigned int ia_valid = attr->ia_valid;
if (ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
i_uid_update(mnt_userns, attr, inode);
i_gid_update(mnt_userns, attr, inode);
if (ia_valid & ATTR_ATIME)
inode->i_atime = attr->ia_atime;
if (ia_valid & ATTR_MTIME)
@@ -254,8 +249,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
inode->i_ctime = attr->ia_ctime;
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) &&
vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
if (!vfsgid_in_group_p(vfsgid) &&
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
mode &= ~S_ISGID;
inode->i_mode = mode;
@@ -306,9 +301,6 @@ EXPORT_SYMBOL(may_setattr);
* retry. Because breaking a delegation may take a long time, the
* caller should drop the i_mutex before doing so.
*
* If file ownership is changed notify_change() doesn't map ia_uid and
* ia_gid. It will asssume the caller has already provided the intended values.
*
* Alternatively, a caller may pass NULL for delegated_inode. This may
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported. Also, passing NULL is fine for callers holding
@@ -397,23 +389,25 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry,
* namespace of the superblock.
*/
if (ia_valid & ATTR_UID &&
!kuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid))
!vfsuid_has_fsmapping(mnt_userns, inode->i_sb->s_user_ns,
attr->ia_vfsuid))
return -EOVERFLOW;
if (ia_valid & ATTR_GID &&
!kgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid))
!vfsgid_has_fsmapping(mnt_userns, inode->i_sb->s_user_ns,
attr->ia_vfsgid))
return -EOVERFLOW;
/* Don't allow modifications of files with invalid uids or
* gids unless those uids & gids are being made valid.
*/
if (!(ia_valid & ATTR_UID) &&
!uid_valid(i_uid_into_mnt(mnt_userns, inode)))
!vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)))
return -EOVERFLOW;
if (!(ia_valid & ATTR_GID) &&
!gid_valid(i_gid_into_mnt(mnt_userns, inode)))
!vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode)))
return -EOVERFLOW;
error = security_inode_setattr(dentry, attr);
error = security_inode_setattr(mnt_userns, dentry, attr);
if (error)
return error;
error = try_break_deleg(inode, delegated_inode);

View File

@@ -1679,14 +1679,14 @@ int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (error)
return error;
if (is_quota_modification(inode, iattr)) {
if (is_quota_modification(mnt_userns, inode, iattr)) {
error = dquot_initialize(inode);
if (error)
return error;
}
if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) ||
(iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) {
error = dquot_transfer(inode, iattr);
if (i_uid_needs_update(mnt_userns, iattr, inode) ||
i_gid_needs_update(mnt_userns, iattr, inode)) {
error = dquot_transfer(mnt_userns, inode, iattr);
if (error)
return error;
}

View File

@@ -5350,14 +5350,14 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (error)
return error;
if (is_quota_modification(inode, attr)) {
if (is_quota_modification(mnt_userns, inode, attr)) {
error = dquot_initialize(inode);
if (error)
return error;
}
if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
(ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
if (i_uid_needs_update(mnt_userns, attr, inode) ||
i_gid_needs_update(mnt_userns, attr, inode)) {
handle_t *handle;
/* (user+group)*(old+new) structure, inode write (sb,
@@ -5374,7 +5374,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
* counts xattr inode references.
*/
down_read(&EXT4_I(inode)->xattr_sem);
error = dquot_transfer(inode, attr);
error = dquot_transfer(mnt_userns, inode, attr);
up_read(&EXT4_I(inode)->xattr_sem);
if (error) {
@@ -5383,10 +5383,8 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
}
/* Update corresponding info in inode so that everything is in
* one transaction */
if (attr->ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
i_uid_update(mnt_userns, attr, inode);
i_gid_update(mnt_userns, attr, inode);
error = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle);
if (unlikely(error)) {

View File

@@ -861,10 +861,8 @@ static void __setattr_copy(struct user_namespace *mnt_userns,
{
unsigned int ia_valid = attr->ia_valid;
if (ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
i_uid_update(mnt_userns, attr, inode);
i_gid_update(mnt_userns, attr, inode);
if (ia_valid & ATTR_ATIME)
inode->i_atime = attr->ia_atime;
if (ia_valid & ATTR_MTIME)
@@ -917,17 +915,15 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (err)
return err;
if (is_quota_modification(inode, attr)) {
if (is_quota_modification(mnt_userns, inode, attr)) {
err = f2fs_dquot_initialize(inode);
if (err)
return err;
}
if ((attr->ia_valid & ATTR_UID &&
!uid_eq(attr->ia_uid, inode->i_uid)) ||
(attr->ia_valid & ATTR_GID &&
!gid_eq(attr->ia_gid, inode->i_gid))) {
if (i_uid_needs_update(mnt_userns, attr, inode) ||
i_gid_needs_update(mnt_userns, attr, inode)) {
f2fs_lock_op(F2FS_I_SB(inode));
err = dquot_transfer(inode, attr);
err = dquot_transfer(mnt_userns, inode, attr);
if (err) {
set_sbi_flag(F2FS_I_SB(inode),
SBI_QUOTA_NEED_REPAIR);
@@ -938,10 +934,8 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
* update uid/gid under lock_op(), so that dquot and inode can
* be updated atomically.
*/
if (attr->ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
i_uid_update(mnt_userns, attr, inode);
i_gid_update(mnt_userns, attr, inode);
f2fs_mark_inode_dirty_sync(inode, true);
f2fs_unlock_op(F2FS_I_SB(inode));
}

View File

@@ -255,18 +255,18 @@ static int recover_quota_data(struct inode *inode, struct page *page)
memset(&attr, 0, sizeof(attr));
attr.ia_uid = make_kuid(inode->i_sb->s_user_ns, i_uid);
attr.ia_gid = make_kgid(inode->i_sb->s_user_ns, i_gid);
attr.ia_vfsuid = VFSUIDT_INIT(make_kuid(inode->i_sb->s_user_ns, i_uid));
attr.ia_vfsgid = VFSGIDT_INIT(make_kgid(inode->i_sb->s_user_ns, i_gid));
if (!uid_eq(attr.ia_uid, inode->i_uid))
if (!vfsuid_eq(attr.ia_vfsuid, i_uid_into_vfsuid(&init_user_ns, inode)))
attr.ia_valid |= ATTR_UID;
if (!gid_eq(attr.ia_gid, inode->i_gid))
if (!vfsgid_eq(attr.ia_vfsgid, i_gid_into_vfsgid(&init_user_ns, inode)))
attr.ia_valid |= ATTR_GID;
if (!attr.ia_valid)
return 0;
err = dquot_transfer(inode, &attr);
err = dquot_transfer(&init_user_ns, inode, &attr);
if (err)
set_sbi_flag(F2FS_I_SB(inode), SBI_QUOTA_NEED_REPAIR);
return err;

View File

@@ -90,7 +90,8 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
* out the RO attribute for checking by the security
* module, just because it maps to a file mode.
*/
err = security_inode_setattr(file->f_path.dentry, &ia);
err = security_inode_setattr(file_mnt_user_ns(file),
file->f_path.dentry, &ia);
if (err)
goto out_unlock_inode;
@@ -516,9 +517,11 @@ int fat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
}
if (((attr->ia_valid & ATTR_UID) &&
(!uid_eq(attr->ia_uid, sbi->options.fs_uid))) ||
(!uid_eq(from_vfsuid(mnt_userns, i_user_ns(inode), attr->ia_vfsuid),
sbi->options.fs_uid))) ||
((attr->ia_valid & ATTR_GID) &&
(!gid_eq(attr->ia_gid, sbi->options.fs_gid))) ||
(!gid_eq(from_vfsgid(mnt_userns, i_user_ns(inode), attr->ia_vfsgid),
sbi->options.fs_gid))) ||
((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~FAT_VALID_MODE)))
error = -EPERM;

View File

@@ -95,14 +95,14 @@ int jfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (rc)
return rc;
if (is_quota_modification(inode, iattr)) {
if (is_quota_modification(mnt_userns, inode, iattr)) {
rc = dquot_initialize(inode);
if (rc)
return rc;
}
if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) ||
(iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) {
rc = dquot_transfer(inode, iattr);
rc = dquot_transfer(mnt_userns, inode, iattr);
if (rc)
return rc;
}

View File

@@ -1146,7 +1146,7 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (status)
return status;
if (is_quota_modification(inode, attr)) {
if (is_quota_modification(mnt_userns, inode, attr)) {
status = dquot_initialize(inode);
if (status)
return status;

View File

@@ -664,6 +664,42 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)
return do_fchmodat(AT_FDCWD, filename, mode);
}
/**
* setattr_vfsuid - check and set ia_fsuid attribute
* @kuid: new inode owner
*
* Check whether @kuid is valid and if so generate and set vfsuid_t in
* ia_vfsuid.
*
* Return: true if @kuid is valid, false if not.
*/
static inline bool setattr_vfsuid(struct iattr *attr, kuid_t kuid)
{
if (!uid_valid(kuid))
return false;
attr->ia_valid |= ATTR_UID;
attr->ia_vfsuid = VFSUIDT_INIT(kuid);
return true;
}
/**
* setattr_vfsgid - check and set ia_fsgid attribute
* @kgid: new inode owner
*
* Check whether @kgid is valid and if so generate and set vfsgid_t in
* ia_vfsgid.
*
* Return: true if @kgid is valid, false if not.
*/
static inline bool setattr_vfsgid(struct iattr *attr, kgid_t kgid)
{
if (!gid_valid(kgid))
return false;
attr->ia_valid |= ATTR_GID;
attr->ia_vfsgid = VFSGIDT_INIT(kgid);
return true;
}
int chown_common(const struct path *path, uid_t user, gid_t group)
{
struct user_namespace *mnt_userns, *fs_userns;
@@ -679,28 +715,22 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
mnt_userns = mnt_user_ns(path->mnt);
fs_userns = i_user_ns(inode);
uid = mapped_kuid_user(mnt_userns, fs_userns, uid);
gid = mapped_kgid_user(mnt_userns, fs_userns, gid);
retry_deleg:
newattrs.ia_valid = ATTR_CTIME;
if (user != (uid_t) -1) {
if (!uid_valid(uid))
return -EINVAL;
newattrs.ia_valid |= ATTR_UID;
newattrs.ia_uid = uid;
}
if (group != (gid_t) -1) {
if (!gid_valid(gid))
return -EINVAL;
newattrs.ia_valid |= ATTR_GID;
newattrs.ia_gid = gid;
}
if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
return -EINVAL;
if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
return -EINVAL;
if (!S_ISDIR(inode->i_mode))
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
inode_lock(inode);
error = security_path_chown(path, uid, gid);
/* Continue to send actual fs values, not the mount values. */
error = security_path_chown(
path,
from_vfsuid(mnt_userns, fs_userns, newattrs.ia_vfsuid),
from_vfsgid(mnt_userns, fs_userns, newattrs.ia_vfsgid));
if (!error)
error = notify_change(mnt_userns, path->dentry, &newattrs,
&delegated_inode);

View File

@@ -331,8 +331,8 @@ int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upperdentry,
if (!err) {
struct iattr attr = {
.ia_valid = ATTR_UID | ATTR_GID,
.ia_uid = stat->uid,
.ia_gid = stat->gid,
.ia_vfsuid = VFSUIDT_INIT(stat->uid),
.ia_vfsgid = VFSGIDT_INIT(stat->gid),
};
err = ovl_do_notify_change(ofs, upperdentry, &attr);
}

View File

@@ -139,17 +139,7 @@ static inline int ovl_do_notify_change(struct ovl_fs *ofs,
struct dentry *upperdentry,
struct iattr *attr)
{
struct user_namespace *upper_mnt_userns = ovl_upper_mnt_userns(ofs);
struct user_namespace *fs_userns = i_user_ns(d_inode(upperdentry));
if (attr->ia_valid & ATTR_UID)
attr->ia_uid = mapped_kuid_user(upper_mnt_userns,
fs_userns, attr->ia_uid);
if (attr->ia_valid & ATTR_GID)
attr->ia_gid = mapped_kgid_user(upper_mnt_userns,
fs_userns, attr->ia_gid);
return notify_change(upper_mnt_userns, upperdentry, attr, NULL);
return notify_change(ovl_upper_mnt_userns(ofs), upperdentry, attr, NULL);
}
static inline int ovl_do_rmdir(struct ovl_fs *ofs,

View File

@@ -2085,7 +2085,8 @@ EXPORT_SYMBOL(__dquot_transfer);
/* Wrapper for transferring ownership of an inode for uid/gid only
* Called from FSXXX_setattr()
*/
int dquot_transfer(struct inode *inode, struct iattr *iattr)
int dquot_transfer(struct user_namespace *mnt_userns, struct inode *inode,
struct iattr *iattr)
{
struct dquot *transfer_to[MAXQUOTAS] = {};
struct dquot *dquot;
@@ -2095,8 +2096,11 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
if (!dquot_active(inode))
return 0;
if (iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)){
dquot = dqget(sb, make_kqid_uid(iattr->ia_uid));
if (i_uid_needs_update(mnt_userns, iattr, inode)) {
kuid_t kuid = from_vfsuid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsuid);
dquot = dqget(sb, make_kqid_uid(kuid));
if (IS_ERR(dquot)) {
if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot);
@@ -2106,8 +2110,11 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
}
transfer_to[USRQUOTA] = dquot;
}
if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid)){
dquot = dqget(sb, make_kqid_gid(iattr->ia_gid));
if (i_gid_needs_update(mnt_userns, iattr, inode)) {
kgid_t kgid = from_vfsgid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsgid);
dquot = dqget(sb, make_kqid_gid(kgid));
if (IS_ERR(dquot)) {
if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot);

View File

@@ -3276,7 +3276,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
/* must be turned off for recursive notify_change calls */
ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID);
if (is_quota_modification(inode, attr)) {
if (is_quota_modification(mnt_userns, inode, attr)) {
error = dquot_initialize(inode);
if (error)
return error;
@@ -3359,7 +3359,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
reiserfs_write_unlock(inode->i_sb);
if (error)
goto out;
error = dquot_transfer(inode, attr);
error = dquot_transfer(mnt_userns, inode, attr);
reiserfs_write_lock(inode->i_sb);
if (error) {
journal_end(&th);

View File

@@ -667,13 +667,15 @@ xfs_setattr_nonsize(
uint qflags = 0;
if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) {
uid = iattr->ia_uid;
uid = from_vfsuid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsuid);
qflags |= XFS_QMOPT_UQUOTA;
} else {
uid = inode->i_uid;
}
if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) {
gid = iattr->ia_gid;
gid = from_vfsgid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsgid);
qflags |= XFS_QMOPT_GQUOTA;
} else {
gid = inode->i_gid;
@@ -704,13 +706,13 @@ xfs_setattr_nonsize(
* didn't have the inode locked, inode's dquot(s) would have changed
* also.
*/
if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp) &&
!uid_eq(inode->i_uid, iattr->ia_uid)) {
if (XFS_IS_UQUOTA_ON(mp) &&
i_uid_needs_update(mnt_userns, iattr, inode)) {
ASSERT(udqp);
old_udqp = xfs_qm_vop_chown(tp, ip, &ip->i_udquot, udqp);
}
if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp) &&
!gid_eq(inode->i_gid, iattr->ia_gid)) {
if (XFS_IS_GQUOTA_ON(mp) &&
i_gid_needs_update(mnt_userns, iattr, inode)) {
ASSERT(xfs_has_pquotino(mp) || !XFS_IS_PQUOTA_ON(mp));
ASSERT(gdqp);
old_gdqp = xfs_qm_vop_chown(tp, ip, &ip->i_gdquot, gdqp);

View File

@@ -616,7 +616,7 @@ static int zonefs_inode_setattr(struct user_namespace *mnt_userns,
!uid_eq(iattr->ia_uid, inode->i_uid)) ||
((iattr->ia_valid & ATTR_GID) &&
!gid_eq(iattr->ia_gid, inode->i_gid))) {
ret = dquot_transfer(inode, iattr);
ret = dquot_transfer(mnt_userns, inode, iattr);
if (ret)
return ret;
}

View File

@@ -21,7 +21,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
void *xattr_value,
size_t xattr_value_len,
struct integrity_iint_cache *iint);
extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
extern int evm_inode_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *attr);
extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
extern int evm_inode_setxattr(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *name,
@@ -68,7 +69,8 @@ static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
}
#endif
static inline int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
static inline int evm_inode_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *attr)
{
return 0;
}

View File

@@ -221,8 +221,26 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
struct iattr {
unsigned int ia_valid;
umode_t ia_mode;
kuid_t ia_uid;
kgid_t ia_gid;
/*
* The two anonymous unions wrap structures with the same member.
*
* Filesystems raising FS_ALLOW_IDMAP need to use ia_vfs{g,u}id which
* are a dedicated type requiring the filesystem to use the dedicated
* helpers. Other filesystem can continue to use ia_{g,u}id until they
* have been ported.
*
* They always contain the same value. In other words FS_ALLOW_IDMAP
* pass down the same value on idmapped mounts as they would on regular
* mounts.
*/
union {
kuid_t ia_uid;
vfsuid_t ia_vfsuid;
};
union {
kgid_t ia_gid;
vfsgid_t ia_vfsgid;
};
loff_t ia_size;
struct timespec64 ia_atime;
struct timespec64 ia_mtime;
@@ -1600,13 +1618,68 @@ static inline void i_gid_write(struct inode *inode, gid_t gid)
* @mnt_userns: user namespace of the mount the inode was found from
* @inode: inode to map
*
* Note, this will eventually be removed completely in favor of the type-safe
* i_uid_into_vfsuid().
*
* Return: the inode's i_uid mapped down according to @mnt_userns.
* If the inode's i_uid has no mapping INVALID_UID is returned.
*/
static inline kuid_t i_uid_into_mnt(struct user_namespace *mnt_userns,
const struct inode *inode)
{
return mapped_kuid_fs(mnt_userns, i_user_ns(inode), inode->i_uid);
return AS_KUIDT(make_vfsuid(mnt_userns, i_user_ns(inode), inode->i_uid));
}
/**
* i_uid_into_vfsuid - map an inode's i_uid down into a mnt_userns
* @mnt_userns: user namespace of the mount the inode was found from
* @inode: inode to map
*
* Return: whe inode's i_uid mapped down according to @mnt_userns.
* If the inode's i_uid has no mapping INVALID_VFSUID is returned.
*/
static inline vfsuid_t i_uid_into_vfsuid(struct user_namespace *mnt_userns,
const struct inode *inode)
{
return make_vfsuid(mnt_userns, i_user_ns(inode), inode->i_uid);
}
/**
* i_uid_needs_update - check whether inode's i_uid needs to be updated
* @mnt_userns: user namespace of the mount the inode was found from
* @attr: the new attributes of @inode
* @inode: the inode to update
*
* Check whether the $inode's i_uid field needs to be updated taking idmapped
* mounts into account if the filesystem supports it.
*
* Return: true if @inode's i_uid field needs to be updated, false if not.
*/
static inline bool i_uid_needs_update(struct user_namespace *mnt_userns,
const struct iattr *attr,
const struct inode *inode)
{
return ((attr->ia_valid & ATTR_UID) &&
!vfsuid_eq(attr->ia_vfsuid,
i_uid_into_vfsuid(mnt_userns, inode)));
}
/**
* i_uid_update - update @inode's i_uid field
* @mnt_userns: user namespace of the mount the inode was found from
* @attr: the new attributes of @inode
* @inode: the inode to update
*
* Safely update @inode's i_uid field translating the vfsuid of any idmapped
* mount into the filesystem kuid.
*/
static inline void i_uid_update(struct user_namespace *mnt_userns,
const struct iattr *attr,
struct inode *inode)
{
if (attr->ia_valid & ATTR_UID)
inode->i_uid = from_vfsuid(mnt_userns, i_user_ns(inode),
attr->ia_vfsuid);
}
/**
@@ -1614,13 +1687,68 @@ static inline kuid_t i_uid_into_mnt(struct user_namespace *mnt_userns,
* @mnt_userns: user namespace of the mount the inode was found from
* @inode: inode to map
*
* Note, this will eventually be removed completely in favor of the type-safe
* i_gid_into_vfsgid().
*
* Return: the inode's i_gid mapped down according to @mnt_userns.
* If the inode's i_gid has no mapping INVALID_GID is returned.
*/
static inline kgid_t i_gid_into_mnt(struct user_namespace *mnt_userns,
const struct inode *inode)
{
return mapped_kgid_fs(mnt_userns, i_user_ns(inode), inode->i_gid);
return AS_KGIDT(make_vfsgid(mnt_userns, i_user_ns(inode), inode->i_gid));
}
/**
* i_gid_into_vfsgid - map an inode's i_gid down into a mnt_userns
* @mnt_userns: user namespace of the mount the inode was found from
* @inode: inode to map
*
* Return: the inode's i_gid mapped down according to @mnt_userns.
* If the inode's i_gid has no mapping INVALID_VFSGID is returned.
*/
static inline vfsgid_t i_gid_into_vfsgid(struct user_namespace *mnt_userns,
const struct inode *inode)
{
return make_vfsgid(mnt_userns, i_user_ns(inode), inode->i_gid);
}
/**
* i_gid_needs_update - check whether inode's i_gid needs to be updated
* @mnt_userns: user namespace of the mount the inode was found from
* @attr: the new attributes of @inode
* @inode: the inode to update
*
* Check whether the $inode's i_gid field needs to be updated taking idmapped
* mounts into account if the filesystem supports it.
*
* Return: true if @inode's i_gid field needs to be updated, false if not.
*/
static inline bool i_gid_needs_update(struct user_namespace *mnt_userns,
const struct iattr *attr,
const struct inode *inode)
{
return ((attr->ia_valid & ATTR_GID) &&
!vfsgid_eq(attr->ia_vfsgid,
i_gid_into_vfsgid(mnt_userns, inode)));
}
/**
* i_gid_update - update @inode's i_gid field
* @mnt_userns: user namespace of the mount the inode was found from
* @attr: the new attributes of @inode
* @inode: the inode to update
*
* Safely update @inode's i_gid field translating the vfsgid of any idmapped
* mount into the filesystem kgid.
*/
static inline void i_gid_update(struct user_namespace *mnt_userns,
const struct iattr *attr,
struct inode *inode)
{
if (attr->ia_valid & ATTR_GID)
inode->i_gid = from_vfsgid(mnt_userns, i_user_ns(inode),
attr->ia_vfsgid);
}
/**
@@ -2195,8 +2323,8 @@ static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags
static inline bool HAS_UNMAPPED_ID(struct user_namespace *mnt_userns,
struct inode *inode)
{
return !uid_valid(i_uid_into_mnt(mnt_userns, inode)) ||
!gid_valid(i_gid_into_mnt(mnt_userns, inode));
return !vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)) ||
!vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode));
}
static inline int iocb_flags(struct file *file);

View File

@@ -13,6 +13,129 @@ struct user_namespace;
*/
extern struct user_namespace init_user_ns;
typedef struct {
uid_t val;
} vfsuid_t;
typedef struct {
gid_t val;
} vfsgid_t;
static_assert(sizeof(vfsuid_t) == sizeof(kuid_t));
static_assert(sizeof(vfsgid_t) == sizeof(kgid_t));
static_assert(offsetof(vfsuid_t, val) == offsetof(kuid_t, val));
static_assert(offsetof(vfsgid_t, val) == offsetof(kgid_t, val));
#ifdef CONFIG_MULTIUSER
static inline uid_t __vfsuid_val(vfsuid_t uid)
{
return uid.val;
}
static inline gid_t __vfsgid_val(vfsgid_t gid)
{
return gid.val;
}
#else
static inline uid_t __vfsuid_val(vfsuid_t uid)
{
return 0;
}
static inline gid_t __vfsgid_val(vfsgid_t gid)
{
return 0;
}
#endif
static inline bool vfsuid_valid(vfsuid_t uid)
{
return __vfsuid_val(uid) != (uid_t)-1;
}
static inline bool vfsgid_valid(vfsgid_t gid)
{
return __vfsgid_val(gid) != (gid_t)-1;
}
static inline bool vfsuid_eq(vfsuid_t left, vfsuid_t right)
{
return vfsuid_valid(left) && __vfsuid_val(left) == __vfsuid_val(right);
}
static inline bool vfsgid_eq(vfsgid_t left, vfsgid_t right)
{
return vfsgid_valid(left) && __vfsgid_val(left) == __vfsgid_val(right);
}
/**
* vfsuid_eq_kuid - check whether kuid and vfsuid have the same value
* @vfsuid: the vfsuid to compare
* @kuid: the kuid to compare
*
* Check whether @vfsuid and @kuid have the same values.
*
* Return: true if @vfsuid and @kuid have the same value, false if not.
* Comparison between two invalid uids returns false.
*/
static inline bool vfsuid_eq_kuid(vfsuid_t vfsuid, kuid_t kuid)
{
return vfsuid_valid(vfsuid) && __vfsuid_val(vfsuid) == __kuid_val(kuid);
}
/**
* vfsgid_eq_kgid - check whether kgid and vfsgid have the same value
* @vfsgid: the vfsgid to compare
* @kgid: the kgid to compare
*
* Check whether @vfsgid and @kgid have the same values.
*
* Return: true if @vfsgid and @kgid have the same value, false if not.
* Comparison between two invalid gids returns false.
*/
static inline bool vfsgid_eq_kgid(vfsgid_t vfsgid, kgid_t kgid)
{
return vfsgid_valid(vfsgid) && __vfsgid_val(vfsgid) == __kgid_val(kgid);
}
/*
* vfs{g,u}ids are created from k{g,u}ids.
* We don't allow them to be created from regular {u,g}id.
*/
#define VFSUIDT_INIT(val) (vfsuid_t){ __kuid_val(val) }
#define VFSGIDT_INIT(val) (vfsgid_t){ __kgid_val(val) }
#define INVALID_VFSUID VFSUIDT_INIT(INVALID_UID)
#define INVALID_VFSGID VFSGIDT_INIT(INVALID_GID)
/*
* Allow a vfs{g,u}id to be used as a k{g,u}id where we want to compare
* whether the mapped value is identical to value of a k{g,u}id.
*/
#define AS_KUIDT(val) (kuid_t){ __vfsuid_val(val) }
#define AS_KGIDT(val) (kgid_t){ __vfsgid_val(val) }
#ifdef CONFIG_MULTIUSER
/**
* vfsgid_in_group_p() - check whether a vfsuid matches the caller's groups
* @vfsgid: the mnt gid to match
*
* This function can be used to determine whether @vfsuid matches any of the
* caller's groups.
*
* Return: 1 if vfsuid matches caller's groups, 0 if not.
*/
static inline int vfsgid_in_group_p(vfsgid_t vfsgid)
{
return in_group_p(AS_KGIDT(vfsgid));
}
#else
static inline int vfsgid_in_group_p(vfsgid_t vfsgid)
{
return 1;
}
#endif
/**
* initial_idmapping - check whether this is the initial mapping
* @ns: idmapping to check
@@ -48,7 +171,7 @@ static inline bool no_idmapping(const struct user_namespace *mnt_userns,
}
/**
* mapped_kuid_fs - map a filesystem kuid into a mnt_userns
* make_vfsuid - map a filesystem kuid into a mnt_userns
* @mnt_userns: the mount's idmapping
* @fs_userns: the filesystem's idmapping
* @kuid : kuid to be mapped
@@ -67,25 +190,33 @@ static inline bool no_idmapping(const struct user_namespace *mnt_userns,
* If @kuid has no mapping in either @mnt_userns or @fs_userns INVALID_UID is
* returned.
*/
static inline kuid_t mapped_kuid_fs(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kuid_t kuid)
static inline vfsuid_t make_vfsuid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kuid_t kuid)
{
uid_t uid;
if (no_idmapping(mnt_userns, fs_userns))
return kuid;
return VFSUIDT_INIT(kuid);
if (initial_idmapping(fs_userns))
uid = __kuid_val(kuid);
else
uid = from_kuid(fs_userns, kuid);
if (uid == (uid_t)-1)
return INVALID_UID;
return make_kuid(mnt_userns, uid);
return INVALID_VFSUID;
return VFSUIDT_INIT(make_kuid(mnt_userns, uid));
}
static inline kuid_t mapped_kuid_fs(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kuid_t kuid)
{
return AS_KUIDT(make_vfsuid(mnt_userns, fs_userns, kuid));
}
/**
* mapped_kgid_fs - map a filesystem kgid into a mnt_userns
* make_vfsgid - map a filesystem kgid into a mnt_userns
* @mnt_userns: the mount's idmapping
* @fs_userns: the filesystem's idmapping
* @kgid : kgid to be mapped
@@ -104,21 +235,56 @@ static inline kuid_t mapped_kuid_fs(struct user_namespace *mnt_userns,
* If @kgid has no mapping in either @mnt_userns or @fs_userns INVALID_GID is
* returned.
*/
static inline kgid_t mapped_kgid_fs(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kgid_t kgid)
static inline vfsgid_t make_vfsgid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kgid_t kgid)
{
gid_t gid;
if (no_idmapping(mnt_userns, fs_userns))
return kgid;
return VFSGIDT_INIT(kgid);
if (initial_idmapping(fs_userns))
gid = __kgid_val(kgid);
else
gid = from_kgid(fs_userns, kgid);
if (gid == (gid_t)-1)
return INVALID_GID;
return make_kgid(mnt_userns, gid);
return INVALID_VFSGID;
return VFSGIDT_INIT(make_kgid(mnt_userns, gid));
}
static inline kgid_t mapped_kgid_fs(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kgid_t kgid)
{
return AS_KGIDT(make_vfsgid(mnt_userns, fs_userns, kgid));
}
/**
* from_vfsuid - map a vfsuid into the filesystem idmapping
* @mnt_userns: the mount's idmapping
* @fs_userns: the filesystem's idmapping
* @vfsuid : vfsuid to be mapped
*
* Map @vfsuid into the filesystem idmapping. This function has to be used in
* order to e.g. write @vfsuid to inode->i_uid.
*
* Return: @vfsuid mapped into the filesystem idmapping
*/
static inline kuid_t from_vfsuid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
vfsuid_t vfsuid)
{
uid_t uid;
if (no_idmapping(mnt_userns, fs_userns))
return AS_KUIDT(vfsuid);
uid = from_kuid(mnt_userns, AS_KUIDT(vfsuid));
if (uid == (uid_t)-1)
return INVALID_UID;
if (initial_idmapping(fs_userns))
return KUIDT_INIT(uid);
return make_kuid(fs_userns, uid);
}
/**
@@ -145,16 +311,53 @@ static inline kuid_t mapped_kuid_user(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kuid_t kuid)
{
uid_t uid;
return from_vfsuid(mnt_userns, fs_userns, VFSUIDT_INIT(kuid));
}
/**
* vfsuid_has_fsmapping - check whether a vfsuid maps into the filesystem
* @mnt_userns: the mount's idmapping
* @fs_userns: the filesystem's idmapping
* @vfsuid: vfsuid to be mapped
*
* Check whether @vfsuid has a mapping in the filesystem idmapping. Use this
* function to check whether the filesystem idmapping has a mapping for
* @vfsuid.
*
* Return: true if @vfsuid has a mapping in the filesystem, false if not.
*/
static inline bool vfsuid_has_fsmapping(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
vfsuid_t vfsuid)
{
return uid_valid(from_vfsuid(mnt_userns, fs_userns, vfsuid));
}
/**
* from_vfsgid - map a vfsgid into the filesystem idmapping
* @mnt_userns: the mount's idmapping
* @fs_userns: the filesystem's idmapping
* @vfsgid : vfsgid to be mapped
*
* Map @vfsgid into the filesystem idmapping. This function has to be used in
* order to e.g. write @vfsgid to inode->i_gid.
*
* Return: @vfsgid mapped into the filesystem idmapping
*/
static inline kgid_t from_vfsgid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
vfsgid_t vfsgid)
{
gid_t gid;
if (no_idmapping(mnt_userns, fs_userns))
return kuid;
uid = from_kuid(mnt_userns, kuid);
if (uid == (uid_t)-1)
return INVALID_UID;
return AS_KGIDT(vfsgid);
gid = from_kgid(mnt_userns, AS_KGIDT(vfsgid));
if (gid == (gid_t)-1)
return INVALID_GID;
if (initial_idmapping(fs_userns))
return KUIDT_INIT(uid);
return make_kuid(fs_userns, uid);
return KGIDT_INIT(gid);
return make_kgid(fs_userns, gid);
}
/**
@@ -181,16 +384,26 @@ static inline kgid_t mapped_kgid_user(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
kgid_t kgid)
{
gid_t gid;
return from_vfsgid(mnt_userns, fs_userns, VFSGIDT_INIT(kgid));
}
if (no_idmapping(mnt_userns, fs_userns))
return kgid;
gid = from_kgid(mnt_userns, kgid);
if (gid == (gid_t)-1)
return INVALID_GID;
if (initial_idmapping(fs_userns))
return KGIDT_INIT(gid);
return make_kgid(fs_userns, gid);
/**
* vfsgid_has_fsmapping - check whether a vfsgid maps into the filesystem
* @mnt_userns: the mount's idmapping
* @fs_userns: the filesystem's idmapping
* @vfsgid: vfsgid to be mapped
*
* Check whether @vfsgid has a mapping in the filesystem idmapping. Use this
* function to check whether the filesystem idmapping has a mapping for
* @vfsgid.
*
* Return: true if @vfsgid has a mapping in the filesystem, false if not.
*/
static inline bool vfsgid_has_fsmapping(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns,
vfsgid_t vfsgid)
{
return gid_valid(from_vfsgid(mnt_userns, fs_userns, vfsgid));
}
/**
@@ -209,7 +422,8 @@ static inline kgid_t mapped_kgid_user(struct user_namespace *mnt_userns,
static inline kuid_t mapped_fsuid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns)
{
return mapped_kuid_user(mnt_userns, fs_userns, current_fsuid());
return from_vfsuid(mnt_userns, fs_userns,
VFSUIDT_INIT(current_fsuid()));
}
/**
@@ -228,7 +442,8 @@ static inline kuid_t mapped_fsuid(struct user_namespace *mnt_userns,
static inline kgid_t mapped_fsgid(struct user_namespace *mnt_userns,
struct user_namespace *fs_userns)
{
return mapped_kgid_user(mnt_userns, fs_userns, current_fsgid());
return from_vfsgid(mnt_userns, fs_userns,
VFSGIDT_INIT(current_fsgid()));
}
#endif /* _LINUX_MNT_IDMAPPING_H */

View File

@@ -20,11 +20,12 @@ static inline struct quota_info *sb_dqopt(struct super_block *sb)
}
/* i_mutex must being held */
static inline bool is_quota_modification(struct inode *inode, struct iattr *ia)
static inline bool is_quota_modification(struct user_namespace *mnt_userns,
struct inode *inode, struct iattr *ia)
{
return (ia->ia_valid & ATTR_SIZE) ||
(ia->ia_valid & ATTR_UID && !uid_eq(ia->ia_uid, inode->i_uid)) ||
(ia->ia_valid & ATTR_GID && !gid_eq(ia->ia_gid, inode->i_gid));
return ((ia->ia_valid & ATTR_SIZE) ||
i_uid_needs_update(mnt_userns, ia, inode) ||
i_gid_needs_update(mnt_userns, ia, inode));
}
#if defined(CONFIG_QUOTA)
@@ -115,7 +116,8 @@ int dquot_set_dqblk(struct super_block *sb, struct kqid id,
struct qc_dqblk *di);
int __dquot_transfer(struct inode *inode, struct dquot **transfer_to);
int dquot_transfer(struct inode *inode, struct iattr *iattr);
int dquot_transfer(struct user_namespace *mnt_userns, struct inode *inode,
struct iattr *iattr);
static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
{
@@ -234,7 +236,8 @@ static inline void dquot_free_inode(struct inode *inode)
{
}
static inline int dquot_transfer(struct inode *inode, struct iattr *iattr)
static inline int dquot_transfer(struct user_namespace *mnt_userns,
struct inode *inode, struct iattr *iattr)
{
return 0;
}

View File

@@ -353,7 +353,8 @@ int security_inode_readlink(struct dentry *dentry);
int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
bool rcu);
int security_inode_permission(struct inode *inode, int mask);
int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
int security_inode_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *attr);
int security_inode_getattr(const struct path *path);
int security_inode_setxattr(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *name,
@@ -848,8 +849,9 @@ static inline int security_inode_permission(struct inode *inode, int mask)
return 0;
}
static inline int security_inode_setattr(struct dentry *dentry,
struct iattr *attr)
static inline int security_inode_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry,
struct iattr *attr)
{
return 0;
}

View File

@@ -755,13 +755,14 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
}
static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
static int evm_attr_change(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = attr->ia_valid;
if ((!(ia_valid & ATTR_UID) || uid_eq(attr->ia_uid, inode->i_uid)) &&
(!(ia_valid & ATTR_GID) || gid_eq(attr->ia_gid, inode->i_gid)) &&
if (!i_uid_needs_update(mnt_userns, attr, inode) &&
!i_gid_needs_update(mnt_userns, attr, inode) &&
(!(ia_valid & ATTR_MODE) || attr->ia_mode == inode->i_mode))
return 0;
@@ -775,7 +776,8 @@ static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
* Permit update of file attributes when files have a valid EVM signature,
* except in the case of them having an immutable portable signature.
*/
int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
int evm_inode_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
@@ -801,7 +803,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
return 0;
if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
!evm_attr_change(dentry, attr))
!evm_attr_change(mnt_userns, dentry, attr))
return 0;
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),

View File

@@ -1324,7 +1324,8 @@ int security_inode_permission(struct inode *inode, int mask)
return call_int_hook(inode_permission, 0, inode, mask);
}
int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
int security_inode_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *attr)
{
int ret;
@@ -1333,7 +1334,7 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
ret = call_int_hook(inode_setattr, 0, dentry, attr);
if (ret)
return ret;
return evm_inode_setattr(dentry, attr);
return evm_inode_setattr(mnt_userns, dentry, attr);
}
EXPORT_SYMBOL_GPL(security_inode_setattr);