diff options
-rw-r--r-- | Documentation/trace/ftrace.rst | 2 | ||||
-rw-r--r-- | fs/attr.c | 74 | ||||
-rw-r--r-- | fs/coredump.c | 4 | ||||
-rw-r--r-- | fs/exec.c | 16 | ||||
-rw-r--r-- | fs/fuse/acl.c | 2 | ||||
-rw-r--r-- | fs/fuse/file.c | 2 | ||||
-rw-r--r-- | fs/inode.c | 72 | ||||
-rw-r--r-- | fs/internal.h | 10 | ||||
-rw-r--r-- | fs/namei.c | 40 | ||||
-rw-r--r-- | fs/ocfs2/file.c | 4 | ||||
-rw-r--r-- | fs/open.c | 8 | ||||
-rw-r--r-- | fs/overlayfs/file.c | 28 | ||||
-rw-r--r-- | fs/overlayfs/util.c | 9 | ||||
-rw-r--r-- | fs/remap_range.c | 2 | ||||
-rw-r--r-- | fs/stat.c | 7 | ||||
-rw-r--r-- | include/linux/fs.h | 36 | ||||
-rw-r--r-- | include/linux/mnt_idmapping.h | 100 | ||||
-rw-r--r-- | kernel/capability.c | 4 | ||||
-rw-r--r-- | security/apparmor/domain.c | 8 | ||||
-rw-r--r-- | security/apparmor/file.c | 4 | ||||
-rw-r--r-- | security/apparmor/lsm.c | 25 | ||||
-rw-r--r-- | security/commoncap.c | 51 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 34 |
23 files changed, 289 insertions, 253 deletions
diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst index 60bceb018d6a..21f01d32c959 100644 --- a/Documentation/trace/ftrace.rst +++ b/Documentation/trace/ftrace.rst @@ -2940,7 +2940,7 @@ Produces:: bash-1994 [000] .... 4342.324898: ima_get_action <-process_measurement bash-1994 [000] .... 4342.324898: ima_match_policy <-ima_get_action bash-1994 [000] .... 4342.324899: do_truncate <-do_last - bash-1994 [000] .... 4342.324899: should_remove_suid <-do_truncate + bash-1994 [000] .... 4342.324899: setattr_should_drop_suidgid <-do_truncate bash-1994 [000] .... 4342.324899: notify_change <-do_truncate bash-1994 [000] .... 4342.324900: current_fs_time <-notify_change bash-1994 [000] .... 4342.324900: current_kernel_time <-current_fs_time diff --git a/fs/attr.c b/fs/attr.c index 1552a5f23d6b..b45f30e516fa 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -18,6 +18,70 @@ #include <linux/evm.h> #include <linux/ima.h> +#include "internal.h" + +/** + * setattr_should_drop_sgid - determine whether the setgid bit needs to be + * removed + * @mnt_userns: user namespace of the mount @inode was found from + * @inode: inode to check + * + * This function determines whether the setgid bit needs to be removed. + * We retain backwards compatibility and require setgid bit to be removed + * unconditionally if S_IXGRP is set. Otherwise we have the exact same + * requirements as setattr_prepare() and setattr_copy(). + * + * Return: ATTR_KILL_SGID if setgid bit needs to be removed, 0 otherwise. + */ +int setattr_should_drop_sgid(struct user_namespace *mnt_userns, + const struct inode *inode) +{ + umode_t mode = inode->i_mode; + + if (!(mode & S_ISGID)) + return 0; + if (mode & S_IXGRP) + return ATTR_KILL_SGID; + if (!in_group_or_capable(mnt_userns, inode, + i_gid_into_vfsgid(mnt_userns, inode))) + return ATTR_KILL_SGID; + return 0; +} + +/** + * setattr_should_drop_suidgid - determine whether the set{g,u}id bit needs to + * be dropped + * @mnt_userns: user namespace of the mount @inode was found from + * @inode: inode to check + * + * This function determines whether the set{g,u}id bits need to be removed. + * If the setuid bit needs to be removed ATTR_KILL_SUID is returned. If the + * setgid bit needs to be removed ATTR_KILL_SGID is returned. If both + * set{g,u}id bits need to be removed the corresponding mask of both flags is + * returned. + * + * Return: A mask of ATTR_KILL_S{G,U}ID indicating which - if any - setid bits + * to remove, 0 otherwise. + */ +int setattr_should_drop_suidgid(struct user_namespace *mnt_userns, + struct inode *inode) +{ + umode_t mode = inode->i_mode; + int kill = 0; + + /* suid always must be killed */ + if (unlikely(mode & S_ISUID)) + kill = ATTR_KILL_SUID; + + kill |= setattr_should_drop_sgid(mnt_userns, inode); + + if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode))) + return kill; + + return 0; +} +EXPORT_SYMBOL(setattr_should_drop_suidgid); + /** * chown_ok - verify permissions to chown inode * @mnt_userns: user namespace of the mount @inode was found from @@ -140,8 +204,7 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry, vfsgid = i_gid_into_vfsgid(mnt_userns, inode); /* Also check the setgid bit! */ - if (!vfsgid_in_group_p(vfsgid) && - !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID)) + if (!in_group_or_capable(mnt_userns, inode, vfsgid)) attr->ia_mode &= ~S_ISGID; } @@ -251,9 +314,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; - 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)) + if (!in_group_or_capable(mnt_userns, inode, + i_gid_into_vfsgid(mnt_userns, inode))) mode &= ~S_ISGID; inode->i_mode = mode; } @@ -375,7 +437,7 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry, } } if (ia_valid & ATTR_KILL_SGID) { - if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { + if (mode & S_ISGID) { if (!(ia_valid & ATTR_MODE)) { ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = inode->i_mode; diff --git a/fs/coredump.c b/fs/coredump.c index 7bad7785e8e6..a133103eb721 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -716,8 +716,8 @@ void do_coredump(const kernel_siginfo_t *siginfo) * filesystem. */ mnt_userns = file_mnt_user_ns(cprm.file); - if (!uid_eq(i_uid_into_mnt(mnt_userns, inode), - current_fsuid())) { + if (!vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, inode), + current_fsuid())) { pr_info_ratelimited("Core dump to %s aborted: cannot preserve file owner\n", cn.corename); goto close_fail; diff --git a/fs/exec.c b/fs/exec.c index 349a5da91efe..dd91adec7a11 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1591,8 +1591,8 @@ static void bprm_fill_uid(struct linux_binprm *bprm, struct file *file) struct user_namespace *mnt_userns; struct inode *inode = file_inode(file); unsigned int mode; - kuid_t uid; - kgid_t gid; + vfsuid_t vfsuid; + vfsgid_t vfsgid; if (!mnt_may_suid(file->f_path.mnt)) return; @@ -1611,23 +1611,23 @@ static void bprm_fill_uid(struct linux_binprm *bprm, struct file *file) /* reload atomically mode/uid/gid now that lock held */ mode = inode->i_mode; - uid = i_uid_into_mnt(mnt_userns, inode); - gid = i_gid_into_mnt(mnt_userns, inode); + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + vfsgid = i_gid_into_vfsgid(mnt_userns, inode); inode_unlock(inode); /* We ignore suid/sgid if there are no mappings for them in the ns */ - if (!kuid_has_mapping(bprm->cred->user_ns, uid) || - !kgid_has_mapping(bprm->cred->user_ns, gid)) + if (!vfsuid_has_mapping(bprm->cred->user_ns, vfsuid) || + !vfsgid_has_mapping(bprm->cred->user_ns, vfsgid)) return; if (mode & S_ISUID) { bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->euid = uid; + bprm->cred->euid = vfsuid_into_kuid(vfsuid); } if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->egid = gid; + bprm->cred->egid = vfsgid_into_kgid(vfsgid); } } diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c index 8edd0f313515..a4850aee2639 100644 --- a/fs/fuse/acl.c +++ b/fs/fuse/acl.c @@ -99,7 +99,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, return ret; } - if (!in_group_p(i_gid_into_mnt(&init_user_ns, inode)) && + if (!vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) && !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 1a3afd469e3a..97e2d815075d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1313,7 +1313,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) return err; if (fc->handle_killpriv_v2 && - should_remove_suid(file_dentry(file))) { + setattr_should_drop_suidgid(&init_user_ns, file_inode(file))) { goto writethrough; } diff --git a/fs/inode.c b/fs/inode.c index b608528efd3a..757cac29bd5a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1949,40 +1949,12 @@ skip_update: EXPORT_SYMBOL(touch_atime); /* - * The logic we want is - * - * if suid or (sgid and xgrp) - * remove privs - */ -int should_remove_suid(struct dentry *dentry) -{ - umode_t mode = d_inode(dentry)->i_mode; - int kill = 0; - - /* suid always must be killed */ - if (unlikely(mode & S_ISUID)) - kill = ATTR_KILL_SUID; - - /* - * sgid without any exec bits is just a mandatory locking mark; leave - * it alone. If some exec bits are set, it's a real sgid; kill it. - */ - if (unlikely((mode & S_ISGID) && (mode & S_IXGRP))) - kill |= ATTR_KILL_SGID; - - if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode))) - return kill; - - return 0; -} -EXPORT_SYMBOL(should_remove_suid); - -/* * Return mask of changes for notify_change() that need to be done as a * response to write or truncate. Return 0 if nothing has to be changed. * Negative value on error (change should be denied). */ -int dentry_needs_remove_privs(struct dentry *dentry) +int dentry_needs_remove_privs(struct user_namespace *mnt_userns, + struct dentry *dentry) { struct inode *inode = d_inode(dentry); int mask = 0; @@ -1991,7 +1963,7 @@ int dentry_needs_remove_privs(struct dentry *dentry) if (IS_NOSEC(inode)) return 0; - mask = should_remove_suid(dentry); + mask = setattr_should_drop_suidgid(mnt_userns, inode); ret = security_inode_need_killpriv(dentry); if (ret < 0) return ret; @@ -2023,7 +1995,7 @@ static int __file_remove_privs(struct file *file, unsigned int flags) if (IS_NOSEC(inode) || !S_ISREG(inode->i_mode)) return 0; - kill = dentry_needs_remove_privs(dentry); + kill = dentry_needs_remove_privs(file_mnt_user_ns(file), dentry); if (kill < 0) return kill; @@ -2354,15 +2326,15 @@ EXPORT_SYMBOL(inode_init_owner); bool inode_owner_or_capable(struct user_namespace *mnt_userns, const struct inode *inode) { - kuid_t i_uid; + vfsuid_t vfsuid; struct user_namespace *ns; - i_uid = i_uid_into_mnt(mnt_userns, inode); - if (uid_eq(current_fsuid(), i_uid)) + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + if (vfsuid_eq_kuid(vfsuid, current_fsuid())) return true; ns = current_user_ns(); - if (kuid_has_mapping(ns, i_uid) && ns_capable(ns, CAP_FOWNER)) + if (vfsuid_has_mapping(ns, vfsuid) && ns_capable(ns, CAP_FOWNER)) return true; return false; } @@ -2488,6 +2460,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 + * @vfsgid: the new/current vfsgid of @inode + * + * Check wether @vfsgid 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, vfsgid_t vfsgid) +{ + if (vfsgid_in_group_p(vfsgid)) + 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 * @dir: parent directory inode @@ -2508,11 +2502,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))) - return mode; - if (capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID)) + if (in_group_or_capable(mnt_userns, dir, + i_gid_into_vfsgid(mnt_userns, dir))) return mode; - return mode & ~S_ISGID; } EXPORT_SYMBOL(mode_strip_sgid); diff --git a/fs/internal.h b/fs/internal.h index e377eb7bbe7f..0c8812fe7ca4 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -150,7 +150,9 @@ extern int vfs_open(const struct path *, struct file *); * inode.c */ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc); -extern int dentry_needs_remove_privs(struct dentry *dentry); +int dentry_needs_remove_privs(struct user_namespace *, struct dentry *dentry); +bool in_group_or_capable(struct user_namespace *mnt_userns, + const struct inode *inode, vfsgid_t vfsgid); /* * fs-writeback.c @@ -255,3 +257,9 @@ static inline ssize_t do_get_acl(struct user_namespace *mnt_userns, #endif ssize_t __kernel_write_iter(struct file *file, struct iov_iter *from, loff_t *pos); + +/* + * fs/attr.c + */ +int setattr_should_drop_sgid(struct user_namespace *mnt_userns, + const struct inode *inode); diff --git a/fs/namei.c b/fs/namei.c index 1c80fd23cda2..a59db476afc2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -336,11 +336,11 @@ static int acl_permission_check(struct user_namespace *mnt_userns, struct inode *inode, int mask) { unsigned int mode = inode->i_mode; - kuid_t i_uid; + vfsuid_t vfsuid; /* Are we the owner? If so, ACL's don't matter */ - i_uid = i_uid_into_mnt(mnt_userns, inode); - if (likely(uid_eq(current_fsuid(), i_uid))) { + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + if (likely(vfsuid_eq_kuid(vfsuid, current_fsuid()))) { mask &= 7; mode >>= 6; return (mask & ~mode) ? -EACCES : 0; @@ -362,8 +362,8 @@ static int acl_permission_check(struct user_namespace *mnt_userns, * about? Need to check group ownership if so. */ if (mask & (mode ^ (mode >> 3))) { - 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)) mode >>= 3; } @@ -581,7 +581,7 @@ struct nameidata { struct nameidata *saved; unsigned root_seq; int dfd; - kuid_t dir_uid; + vfsuid_t dir_vfsuid; umode_t dir_mode; } __randomize_layout; @@ -1095,15 +1095,15 @@ fs_initcall(init_fs_namei_sysctls); static inline int may_follow_link(struct nameidata *nd, const struct inode *inode) { struct user_namespace *mnt_userns; - kuid_t i_uid; + vfsuid_t vfsuid; if (!sysctl_protected_symlinks) return 0; mnt_userns = mnt_user_ns(nd->path.mnt); - i_uid = i_uid_into_mnt(mnt_userns, inode); + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); /* Allowed if owner and follower match. */ - if (uid_eq(current_cred()->fsuid, i_uid)) + if (vfsuid_eq_kuid(vfsuid, current_fsuid())) return 0; /* Allowed if parent directory not sticky and world-writable. */ @@ -1111,7 +1111,7 @@ static inline int may_follow_link(struct nameidata *nd, const struct inode *inod return 0; /* Allowed if parent directory and link owner match. */ - if (uid_valid(nd->dir_uid) && uid_eq(nd->dir_uid, i_uid)) + if (vfsuid_valid(nd->dir_vfsuid) && vfsuid_eq(nd->dir_vfsuid, vfsuid)) return 0; if (nd->flags & LOOKUP_RCU) @@ -1183,8 +1183,8 @@ int may_linkat(struct user_namespace *mnt_userns, const struct path *link) struct inode *inode = link->dentry->d_inode; /* Inode writeback is not safe when the uid or gid are invalid. */ - if (!uid_valid(i_uid_into_mnt(mnt_userns, inode)) || - !gid_valid(i_gid_into_mnt(mnt_userns, inode))) + if (!vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)) || + !vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode))) return -EOVERFLOW; if (!sysctl_protected_hardlinks) @@ -1232,13 +1232,13 @@ static int may_create_in_sticky(struct user_namespace *mnt_userns, struct nameidata *nd, struct inode *const inode) { umode_t dir_mode = nd->dir_mode; - kuid_t dir_uid = nd->dir_uid; + vfsuid_t dir_vfsuid = nd->dir_vfsuid; if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) || (!sysctl_protected_regular && S_ISREG(inode->i_mode)) || likely(!(dir_mode & S_ISVTX)) || - uid_eq(i_uid_into_mnt(mnt_userns, inode), dir_uid) || - uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode))) + vfsuid_eq(i_uid_into_vfsuid(mnt_userns, inode), dir_vfsuid) || + vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, inode), current_fsuid())) return 0; if (likely(dir_mode & 0002) || @@ -2307,7 +2307,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) OK: /* pathname or trailing symlink, done */ if (!depth) { - nd->dir_uid = i_uid_into_mnt(mnt_userns, nd->inode); + nd->dir_vfsuid = i_uid_into_vfsuid(mnt_userns, nd->inode); nd->dir_mode = nd->inode->i_mode; nd->flags &= ~LOOKUP_PARENT; return 0; @@ -2885,9 +2885,9 @@ int __check_sticky(struct user_namespace *mnt_userns, struct inode *dir, { kuid_t fsuid = current_fsuid(); - if (uid_eq(i_uid_into_mnt(mnt_userns, inode), fsuid)) + if (vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, inode), fsuid)) return 0; - if (uid_eq(i_uid_into_mnt(mnt_userns, dir), fsuid)) + if (vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, dir), fsuid)) return 0; return !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FOWNER); } @@ -2926,8 +2926,8 @@ static int may_delete(struct user_namespace *mnt_userns, struct inode *dir, BUG_ON(victim->d_parent->d_inode != dir); /* Inode writeback is not safe when the uid or gid are invalid. */ - if (!uid_valid(i_uid_into_mnt(mnt_userns, inode)) || - !gid_valid(i_gid_into_mnt(mnt_userns, inode))) + if (!vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)) || + !vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode))) return -EOVERFLOW; audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index af900aaa9275..5c60b6bc85bf 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1991,7 +1991,7 @@ static int __ocfs2_change_file_space(struct file *file, struct inode *inode, } } - if (file && should_remove_suid(file->f_path.dentry)) { + if (file && setattr_should_drop_suidgid(&init_user_ns, file_inode(file))) { ret = __ocfs2_write_remove_suid(inode, di_bh); if (ret) { mlog_errno(ret); @@ -2279,7 +2279,7 @@ static int ocfs2_prepare_inode_for_write(struct file *file, * inode. There's also the dinode i_size state which * can be lost via setattr during extending writes (we * set inode->i_size at the end of a write. */ - if (should_remove_suid(dentry)) { + if (setattr_should_drop_suidgid(&init_user_ns, inode)) { if (meta_level == 0) { ocfs2_inode_unlock_for_extent_tree(inode, &di_bh, diff --git a/fs/open.c b/fs/open.c index a81319b6177f..9d0197db15e7 100644 --- a/fs/open.c +++ b/fs/open.c @@ -54,7 +54,7 @@ int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry, } /* Remove suid, sgid, and file capabilities on truncate too */ - ret = dentry_needs_remove_privs(dentry); + ret = dentry_needs_remove_privs(mnt_userns, dentry); if (ret < 0) return ret; if (ret) @@ -723,10 +723,10 @@ retry_deleg: 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); + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV | + setattr_should_drop_sgid(mnt_userns, inode); /* Continue to send actual fs values, not the mount values. */ error = security_path_chown( path, diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index a1a22f58ba18..d066be3b9226 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -517,9 +517,16 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len const struct cred *old_cred; int ret; + inode_lock(inode); + /* Update mode */ + ovl_copyattr(inode); + ret = file_remove_privs(file); + if (ret) + goto out_unlock; + ret = ovl_real_fdget(file, &real); if (ret) - return ret; + goto out_unlock; old_cred = ovl_override_creds(file_inode(file)->i_sb); ret = vfs_fallocate(real.file, mode, offset, len); @@ -530,6 +537,9 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len fdput(real); +out_unlock: + inode_unlock(inode); + return ret; } @@ -567,14 +577,23 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, const struct cred *old_cred; loff_t ret; + inode_lock(inode_out); + if (op != OVL_DEDUPE) { + /* Update mode */ + ovl_copyattr(inode_out); + ret = file_remove_privs(file_out); + if (ret) + goto out_unlock; + } + ret = ovl_real_fdget(file_out, &real_out); if (ret) - return ret; + goto out_unlock; ret = ovl_real_fdget(file_in, &real_in); if (ret) { fdput(real_out); - return ret; + goto out_unlock; } old_cred = ovl_override_creds(file_inode(file_out)->i_sb); @@ -603,6 +622,9 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, fdput(real_in); fdput(real_out); +out_unlock: + inode_unlock(inode_out); + return ret; } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 81a57a8d80d9..c0c20d33691b 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1104,13 +1104,18 @@ void ovl_copyattr(struct inode *inode) struct path realpath; struct inode *realinode; struct user_namespace *real_mnt_userns; + vfsuid_t vfsuid; + vfsgid_t vfsgid; ovl_i_path_real(inode, &realpath); realinode = d_inode(realpath.dentry); real_mnt_userns = mnt_user_ns(realpath.mnt); - inode->i_uid = i_uid_into_mnt(real_mnt_userns, realinode); - inode->i_gid = i_gid_into_mnt(real_mnt_userns, realinode); + vfsuid = i_uid_into_vfsuid(real_mnt_userns, realinode); + vfsgid = i_gid_into_vfsgid(real_mnt_userns, realinode); + + inode->i_uid = vfsuid_into_kuid(vfsuid); + inode->i_gid = vfsgid_into_kgid(vfsgid); inode->i_mode = realinode->i_mode; inode->i_atime = realinode->i_atime; inode->i_mtime = realinode->i_mtime; diff --git a/fs/remap_range.c b/fs/remap_range.c index 654912d06862..290743c8d226 100644 --- a/fs/remap_range.c +++ b/fs/remap_range.c @@ -429,7 +429,7 @@ static bool allow_file_dedupe(struct file *file) return true; if (file->f_mode & FMODE_WRITE) return true; - if (uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode))) + if (vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, inode), current_fsuid())) return true; if (!inode_permission(mnt_userns, inode, MAY_WRITE)) return true; diff --git a/fs/stat.c b/fs/stat.c index ef50573c72a2..d6cc74ca8486 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -44,12 +44,15 @@ void generic_fillattr(struct user_namespace *mnt_userns, struct inode *inode, struct kstat *stat) { + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode); + stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; stat->mode = inode->i_mode; stat->nlink = inode->i_nlink; - stat->uid = i_uid_into_mnt(mnt_userns, inode); - stat->gid = i_gid_into_mnt(mnt_userns, inode); + stat->uid = vfsuid_into_kuid(vfsuid); + stat->gid = vfsgid_into_kgid(vfsgid); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; diff --git a/include/linux/fs.h b/include/linux/fs.h index 255f6eff89d7..cffc402c2451 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1613,23 +1613,6 @@ static inline void i_gid_write(struct inode *inode, gid_t gid) } /** - * i_uid_into_mnt - 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 - * - * 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 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 @@ -1682,23 +1665,6 @@ static inline void i_uid_update(struct user_namespace *mnt_userns, } /** - * i_gid_into_mnt - 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 - * - * 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 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 @@ -3106,7 +3072,7 @@ extern void __destroy_inode(struct inode *); extern struct inode *new_inode_pseudo(struct super_block *sb); extern struct inode *new_inode(struct super_block *sb); extern void free_inode_nonrcu(struct inode *inode); -extern int should_remove_suid(struct dentry *); +extern int setattr_should_drop_suidgid(struct user_namespace *, struct inode *); extern int file_remove_privs(struct file *); /* diff --git a/include/linux/mnt_idmapping.h b/include/linux/mnt_idmapping.h index f6e5369d2928..c8002294a72d 100644 --- a/include/linux/mnt_idmapping.h +++ b/include/linux/mnt_idmapping.h @@ -98,6 +98,26 @@ static inline bool vfsgid_eq_kgid(vfsgid_t vfsgid, kgid_t kgid) return vfsgid_valid(vfsgid) && __vfsgid_val(vfsgid) == __kgid_val(kgid); } +static inline bool vfsuid_gt_kuid(vfsuid_t vfsuid, kuid_t kuid) +{ + return __vfsuid_val(vfsuid) > __kuid_val(kuid); +} + +static inline bool vfsgid_gt_kgid(vfsgid_t vfsgid, kgid_t kgid) +{ + return __vfsgid_val(vfsgid) > __kgid_val(kgid); +} + +static inline bool vfsuid_lt_kuid(vfsuid_t vfsuid, kuid_t kuid) +{ + return __vfsuid_val(vfsuid) < __kuid_val(kuid); +} + +static inline bool vfsgid_lt_kgid(vfsgid_t vfsgid, kgid_t kgid) +{ + return __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. @@ -208,13 +228,6 @@ static inline vfsuid_t make_vfsuid(struct user_namespace *mnt_userns, 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)); -} - /** * make_vfsgid - map a filesystem kgid into a mnt_userns * @mnt_userns: the mount's idmapping @@ -253,13 +266,6 @@ static inline vfsgid_t make_vfsgid(struct user_namespace *mnt_userns, 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 @@ -288,33 +294,6 @@ static inline kuid_t from_vfsuid(struct user_namespace *mnt_userns, } /** - * mapped_kuid_user - map a user kuid into a mnt_userns - * @mnt_userns: the mount's idmapping - * @fs_userns: the filesystem's idmapping - * @kuid : kuid to be mapped - * - * Use the idmapping of @mnt_userns to remap a @kuid into @fs_userns. Use this - * function when preparing a @kuid to be written to disk or inode. - * - * If no_idmapping() determines that this is not an idmapped mount we can - * simply return @kuid unchanged. - * If initial_idmapping() tells us that the filesystem is not mounted with an - * idmapping we know the value of @kuid won't change when calling - * make_kuid() so we can simply retrieve the value via KUIDT_INIT() - * directly. - * - * Return: @kuid mapped according to @mnt_userns. - * If @kuid has no mapping in either @mnt_userns or @fs_userns INVALID_UID is - * returned. - */ -static inline kuid_t mapped_kuid_user(struct user_namespace *mnt_userns, - struct user_namespace *fs_userns, - kuid_t kuid) -{ - 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 @@ -333,6 +312,12 @@ static inline bool vfsuid_has_fsmapping(struct user_namespace *mnt_userns, return uid_valid(from_vfsuid(mnt_userns, fs_userns, vfsuid)); } +static inline bool vfsuid_has_mapping(struct user_namespace *userns, + vfsuid_t vfsuid) +{ + return from_kuid(userns, AS_KUIDT(vfsuid)) != (uid_t)-1; +} + /** * vfsuid_into_kuid - convert vfsuid into kuid * @vfsuid: the vfsuid to convert @@ -374,33 +359,6 @@ static inline kgid_t from_vfsgid(struct user_namespace *mnt_userns, } /** - * mapped_kgid_user - map a user kgid into a mnt_userns - * @mnt_userns: the mount's idmapping - * @fs_userns: the filesystem's idmapping - * @kgid : kgid to be mapped - * - * Use the idmapping of @mnt_userns to remap a @kgid into @fs_userns. Use this - * function when preparing a @kgid to be written to disk or inode. - * - * If no_idmapping() determines that this is not an idmapped mount we can - * simply return @kgid unchanged. - * If initial_idmapping() tells us that the filesystem is not mounted with an - * idmapping we know the value of @kgid won't change when calling - * make_kgid() so we can simply retrieve the value via KGIDT_INIT() - * directly. - * - * Return: @kgid mapped according to @mnt_userns. - * If @kgid has no mapping in either @mnt_userns or @fs_userns INVALID_GID is - * returned. - */ -static inline kgid_t mapped_kgid_user(struct user_namespace *mnt_userns, - struct user_namespace *fs_userns, - kgid_t kgid) -{ - return from_vfsgid(mnt_userns, fs_userns, VFSGIDT_INIT(kgid)); -} - -/** * vfsgid_has_fsmapping - check whether a vfsgid maps into the filesystem * @mnt_userns: the mount's idmapping * @fs_userns: the filesystem's idmapping @@ -419,6 +377,12 @@ static inline bool vfsgid_has_fsmapping(struct user_namespace *mnt_userns, return gid_valid(from_vfsgid(mnt_userns, fs_userns, vfsgid)); } +static inline bool vfsgid_has_mapping(struct user_namespace *userns, + vfsgid_t vfsgid) +{ + return from_kgid(userns, AS_KGIDT(vfsgid)) != (gid_t)-1; +} + /** * vfsgid_into_kgid - convert vfsgid into kgid * @vfsgid: the vfsgid to convert diff --git a/kernel/capability.c b/kernel/capability.c index 765194f5d678..860fd22117c1 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -489,8 +489,8 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, struct user_namespace *mnt_userns, const struct inode *inode) { - return kuid_has_mapping(ns, i_uid_into_mnt(mnt_userns, inode)) && - kgid_has_mapping(ns, i_gid_into_mnt(mnt_userns, inode)); + return vfsuid_has_mapping(ns, i_uid_into_vfsuid(mnt_userns, inode)) && + vfsgid_has_mapping(ns, i_gid_into_vfsgid(mnt_userns, inode)); } /** diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 91689d34d281..7bafb4c4767c 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -859,10 +859,10 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) const char *info = NULL; int error = 0; bool unsafe = false; - kuid_t i_uid = i_uid_into_mnt(file_mnt_user_ns(bprm->file), - file_inode(bprm->file)); + vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_user_ns(bprm->file), + file_inode(bprm->file)); struct path_cond cond = { - i_uid, + vfsuid_into_kuid(vfsuid), file_inode(bprm->file)->i_mode }; @@ -970,7 +970,7 @@ audit: error = fn_for_each(label, profile, aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC, bprm->filename, NULL, new, - i_uid, info, error)); + vfsuid_into_kuid(vfsuid), info, error)); aa_put_label(new); goto done; } diff --git a/security/apparmor/file.c b/security/apparmor/file.c index e1b7e93602e4..d43679894d23 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -510,8 +510,10 @@ static int __file_path_perm(const char *op, struct aa_label *label, { struct aa_profile *profile; struct aa_perms perms = {}; + vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_user_ns(file), + file_inode(file)); struct path_cond cond = { - .uid = i_uid_into_mnt(file_mnt_user_ns(file), file_inode(file)), + .uid = vfsuid_into_kuid(vfsuid), .mode = file_inode(file)->i_mode }; char *buffer; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f56070270c69..f34675f7c3df 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -225,8 +225,10 @@ static int common_perm(const char *op, const struct path *path, u32 mask, static int common_perm_cond(const char *op, const struct path *path, u32 mask) { struct user_namespace *mnt_userns = mnt_user_ns(path->mnt); + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, + d_backing_inode(path->dentry)); struct path_cond cond = { - i_uid_into_mnt(mnt_userns, d_backing_inode(path->dentry)), + vfsuid_into_kuid(vfsuid), d_backing_inode(path->dentry)->i_mode }; @@ -270,11 +272,13 @@ static int common_perm_rm(const char *op, const struct path *dir, struct inode *inode = d_backing_inode(dentry); struct user_namespace *mnt_userns = mnt_user_ns(dir->mnt); struct path_cond cond = { }; + vfsuid_t vfsuid; if (!inode || !path_mediated_fs(dentry)) return 0; - cond.uid = i_uid_into_mnt(mnt_userns, inode); + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + cond.uid = vfsuid_into_kuid(vfsuid); cond.mode = inode->i_mode; return common_perm_dir_dentry(op, dir, dentry, mask, &cond); @@ -368,20 +372,23 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d label = begin_current_label_crit_section(); if (!unconfined(label)) { struct user_namespace *mnt_userns = mnt_user_ns(old_dir->mnt); + vfsuid_t vfsuid; struct path old_path = { .mnt = old_dir->mnt, .dentry = old_dentry }; struct path new_path = { .mnt = new_dir->mnt, .dentry = new_dentry }; struct path_cond cond = { - i_uid_into_mnt(mnt_userns, d_backing_inode(old_dentry)), - d_backing_inode(old_dentry)->i_mode + .mode = d_backing_inode(old_dentry)->i_mode }; + vfsuid = i_uid_into_vfsuid(mnt_userns, d_backing_inode(old_dentry)); + cond.uid = vfsuid_into_kuid(vfsuid); if (flags & RENAME_EXCHANGE) { struct path_cond cond_exchange = { - i_uid_into_mnt(mnt_userns, d_backing_inode(new_dentry)), - d_backing_inode(new_dentry)->i_mode + .mode = d_backing_inode(new_dentry)->i_mode, }; + vfsuid = i_uid_into_vfsuid(mnt_userns, d_backing_inode(old_dentry)); + cond_exchange.uid = vfsuid_into_kuid(vfsuid); error = aa_path_perm(OP_RENAME_SRC, label, &new_path, 0, MAY_READ | AA_MAY_GETATTR | MAY_WRITE | @@ -447,10 +454,12 @@ static int apparmor_file_open(struct file *file) if (!unconfined(label)) { struct user_namespace *mnt_userns = file_mnt_user_ns(file); struct inode *inode = file_inode(file); + vfsuid_t vfsuid; struct path_cond cond = { - i_uid_into_mnt(mnt_userns, inode), - inode->i_mode + .mode = inode->i_mode, }; + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + cond.uid = vfsuid_into_kuid(vfsuid); error = aa_path_perm(OP_OPEN, label, &file->f_path, 0, aa_map_file_to_perms(file), &cond); diff --git a/security/commoncap.c b/security/commoncap.c index 5fc8986c3c77..b4ce33e20715 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -328,14 +328,16 @@ int cap_inode_killpriv(struct user_namespace *mnt_userns, struct dentry *dentry) return error; } -static bool rootid_owns_currentns(kuid_t kroot) +static bool rootid_owns_currentns(vfsuid_t rootvfsuid) { struct user_namespace *ns; + kuid_t kroot; - if (!uid_valid(kroot)) + if (!vfsuid_valid(rootvfsuid)) return false; - for (ns = current_user_ns(); ; ns = ns->parent) { + kroot = vfsuid_into_kuid(rootvfsuid); + for (ns = current_user_ns();; ns = ns->parent) { if (from_kuid(ns, kroot) == 0) return true; if (ns == &init_user_ns) @@ -381,6 +383,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, { int size, ret; kuid_t kroot; + vfsuid_t vfsroot; u32 nsmagic, magic; uid_t root, mappedroot; char *tmpbuf = NULL; @@ -419,11 +422,11 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, kroot = make_kuid(fs_ns, root); /* If this is an idmapped mount shift the kuid. */ - kroot = mapped_kuid_fs(mnt_userns, fs_ns, kroot); + vfsroot = make_vfsuid(mnt_userns, fs_ns, kroot); /* If the root kuid maps to a valid uid in current ns, then return * this as a nscap. */ - mappedroot = from_kuid(current_user_ns(), kroot); + mappedroot = from_kuid(current_user_ns(), vfsuid_into_kuid(vfsroot)); if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) { size = sizeof(struct vfs_ns_cap_data); if (alloc) { @@ -450,7 +453,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, goto out_free; } - if (!rootid_owns_currentns(kroot)) { + if (!rootid_owns_currentns(vfsroot)) { size = -EOVERFLOW; goto out_free; } @@ -488,29 +491,17 @@ out_free: * @value: vfs caps value which may be modified by this function * @size: size of @ivalue * @task_ns: user namespace of the caller - * @mnt_userns: user namespace of the mount the inode was found from - * @fs_userns: user namespace of the filesystem - * - * 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 - * take care to map the inode according to @mnt_userns before checking - * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs init_user_ns. */ -static kuid_t rootid_from_xattr(const void *value, size_t size, - struct user_namespace *task_ns, - struct user_namespace *mnt_userns, - struct user_namespace *fs_userns) +static vfsuid_t rootid_from_xattr(const void *value, size_t size, + struct user_namespace *task_ns) { const struct vfs_ns_cap_data *nscap = value; - kuid_t rootkid; uid_t rootid = 0; if (size == XATTR_CAPS_SZ_3) rootid = le32_to_cpu(nscap->rootid); - rootkid = make_kuid(task_ns, rootid); - return mapped_kuid_user(mnt_userns, fs_userns, rootkid); + return VFSUIDT_INIT(make_kuid(task_ns, rootid)); } static bool validheader(size_t size, const struct vfs_cap_data *cap) @@ -548,6 +539,7 @@ int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry, struct user_namespace *task_ns = current_user_ns(), *fs_ns = inode->i_sb->s_user_ns; kuid_t rootid; + vfsuid_t vfsrootid; size_t newsize; if (!*ivalue) @@ -561,7 +553,11 @@ int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry, /* user is privileged, just write the v2 */ return size; - rootid = rootid_from_xattr(*ivalue, size, task_ns, mnt_userns, fs_ns); + vfsrootid = rootid_from_xattr(*ivalue, size, task_ns); + if (!vfsuid_valid(vfsrootid)) + return -EINVAL; + + rootid = from_vfsuid(mnt_userns, fs_ns, vfsrootid); if (!uid_valid(rootid)) return -EINVAL; @@ -655,6 +651,7 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, struct vfs_ns_cap_data data, *nscaps = &data; struct vfs_cap_data *caps = (struct vfs_cap_data *) &data; kuid_t rootkuid; + vfsuid_t rootvfsuid; struct user_namespace *fs_ns; memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); @@ -699,11 +696,15 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, default: return -EINVAL; } + + rootvfsuid = make_vfsuid(mnt_userns, fs_ns, rootkuid); + if (!vfsuid_valid(rootvfsuid)) + return -ENODATA; + /* Limit the caps to the mounter of the filesystem * or the more limited uid specified in the xattr. */ - rootkuid = mapped_kuid_fs(mnt_userns, fs_ns, rootkuid); - if (!rootid_owns_currentns(rootkuid)) + if (!rootid_owns_currentns(rootvfsuid)) return -ENODATA; CAP_FOR_EACH_U32(i) { @@ -716,7 +717,7 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; - cpu_caps->rootid = rootkuid; + cpu_caps->rootid = vfsuid_into_kuid(rootvfsuid); return 0; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index a8802b8da946..54c475f98ce1 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -85,8 +85,8 @@ struct ima_rule_entry { kgid_t fgroup; bool (*uid_op)(kuid_t cred_uid, kuid_t rule_uid); /* Handlers for operators */ bool (*gid_op)(kgid_t cred_gid, kgid_t rule_gid); - bool (*fowner_op)(kuid_t cred_uid, kuid_t rule_uid); /* uid_eq(), uid_gt(), uid_lt() */ - bool (*fgroup_op)(kgid_t cred_gid, kgid_t rule_gid); /* gid_eq(), gid_gt(), gid_lt() */ + bool (*fowner_op)(vfsuid_t vfsuid, kuid_t rule_uid); /* vfsuid_eq_kuid(), vfsuid_gt_kuid(), vfsuid_lt_kuid() */ + bool (*fgroup_op)(vfsgid_t vfsgid, kgid_t rule_gid); /* vfsgid_eq_kgid(), vfsgid_gt_kgid(), vfsgid_lt_kgid() */ int pcr; unsigned int allowed_algos; /* bitfield of allowed hash algorithms */ struct { @@ -186,11 +186,11 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, #endif #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT - {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &vfsuid_eq_kuid, .flags = IMA_FOWNER}, #else /* force signature */ - {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &vfsuid_eq_kuid, .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, #endif }; @@ -601,10 +601,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule, return false; } if ((rule->flags & IMA_FOWNER) && - !rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner)) + !rule->fowner_op(i_uid_into_vfsuid(mnt_userns, inode), + rule->fowner)) return false; if ((rule->flags & IMA_FGROUP) && - !rule->fgroup_op(i_gid_into_mnt(mnt_userns, inode), rule->fgroup)) + !rule->fgroup_op(i_gid_into_vfsgid(mnt_userns, inode), + rule->fgroup)) return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; @@ -1371,8 +1373,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->fgroup = INVALID_GID; entry->uid_op = &uid_eq; entry->gid_op = &gid_eq; - entry->fowner_op = &uid_eq; - entry->fgroup_op = &gid_eq; + entry->fowner_op = &vfsuid_eq_kuid; + entry->fgroup_op = &vfsgid_eq_kgid; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -1650,11 +1652,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) } break; case Opt_fowner_gt: - entry->fowner_op = &uid_gt; + entry->fowner_op = &vfsuid_gt_kuid; fallthrough; case Opt_fowner_lt: if (token == Opt_fowner_lt) - entry->fowner_op = &uid_lt; + entry->fowner_op = &vfsuid_lt_kuid; fallthrough; case Opt_fowner_eq: ima_log_string_op(ab, "fowner", args[0].from, token); @@ -1676,11 +1678,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) } break; case Opt_fgroup_gt: - entry->fgroup_op = &gid_gt; + entry->fgroup_op = &vfsgid_gt_kgid; fallthrough; case Opt_fgroup_lt: if (token == Opt_fgroup_lt) - entry->fgroup_op = &gid_lt; + entry->fgroup_op = &vfsgid_lt_kgid; fallthrough; case Opt_fgroup_eq: ima_log_string_op(ab, "fgroup", args[0].from, token); @@ -2151,9 +2153,9 @@ int ima_policy_show(struct seq_file *m, void *v) if (entry->flags & IMA_FOWNER) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); - if (entry->fowner_op == &uid_gt) + if (entry->fowner_op == &vfsuid_gt_kuid) seq_printf(m, pt(Opt_fowner_gt), tbuf); - else if (entry->fowner_op == &uid_lt) + else if (entry->fowner_op == &vfsuid_lt_kuid) seq_printf(m, pt(Opt_fowner_lt), tbuf); else seq_printf(m, pt(Opt_fowner_eq), tbuf); @@ -2162,9 +2164,9 @@ int ima_policy_show(struct seq_file *m, void *v) if (entry->flags & IMA_FGROUP) { snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->fgroup)); - if (entry->fgroup_op == &gid_gt) + if (entry->fgroup_op == &vfsgid_gt_kgid) seq_printf(m, pt(Opt_fgroup_gt), tbuf); - else if (entry->fgroup_op == &gid_lt) + else if (entry->fgroup_op == &vfsgid_lt_kgid) seq_printf(m, pt(Opt_fgroup_lt), tbuf); else seq_printf(m, pt(Opt_fgroup_eq), tbuf); |