From 9c4f28ddfb9c2e674fca24f68f12c1ffbfbffe41 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 26 Jun 2022 20:45:07 +0200 Subject: [PATCH 1/8] mnt_idmapping: add missing helpers Add missing helpers needed to convert all remaining places to the type safe idmapped mount helpers. After the conversion we will remove all the old helpers. Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- include/linux/mnt_idmapping.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/linux/mnt_idmapping.h b/include/linux/mnt_idmapping.h index f6e5369d2928..cd1950ddc6a9 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. @@ -333,6 +353,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 @@ -419,6 +445,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 From a2bd096fb2d7f50fb4db246b33e7bfcf5e2eda3a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 22 Jun 2022 22:12:16 +0200 Subject: [PATCH 2/8] fs: use type safe idmapping helpers We already ported most parts and filesystems over for v6.0 to the new vfs{g,u}id_t type and associated helpers for v6.0. Convert the remaining places so we can remove all the old helpers. This is a non-functional change. Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- fs/coredump.c | 4 ++-- fs/exec.c | 16 ++++++++-------- fs/inode.c | 8 ++++---- fs/namei.c | 40 ++++++++++++++++++++-------------------- fs/remap_range.c | 2 +- fs/stat.c | 7 +++++-- 6 files changed, 40 insertions(+), 37 deletions(-) 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/inode.c b/fs/inode.c index 8c4078889754..757cac29bd5a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2326,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; } diff --git a/fs/namei.c b/fs/namei.c index 578c2110df02..d5c5cb7dd023 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/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; From b7c9b6751242fec703c1b7d1bcfea1d80fe00c8d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 20 Oct 2022 14:50:10 +0200 Subject: [PATCH 3/8] caps: use type safe idmapping helpers We already ported most parts and filesystems over for v6.0 to the new vfs{g,u}id_t type and associated helpers for v6.0. Convert the remaining places so we can remove all the old helpers. This is a non-functional change. Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- kernel/capability.c | 4 ++-- security/commoncap.c | 51 ++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 27 deletions(-) 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/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; } From 5e26a01e56fd03647d14b4461eeb69abde88a4e3 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 26 Jun 2022 18:06:01 +0200 Subject: [PATCH 4/8] apparmor: use type safe idmapping helpers We already ported most parts and filesystems over for v6.0 to the new vfs{g,u}id_t type and associated helpers for v6.0. Convert the remaining places so we can remove all the old helpers. This is a non-functional change. Reviewed-by: Seth Forshee (DigitalOcean) Acked-by: John Johansen Signed-off-by: Christian Brauner (Microsoft) --- security/apparmor/domain.c | 8 ++++---- security/apparmor/file.c | 4 +++- security/apparmor/lsm.c | 25 +++++++++++++++++-------- 3 files changed, 24 insertions(+), 13 deletions(-) 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); From 8e27a7ae3d8699f1988331368ad658510a248c09 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 29 Jun 2022 16:28:04 +0200 Subject: [PATCH 5/8] ima: use type safe idmapping helpers We already ported most parts and filesystems over for v6.0 to the new vfs{g,u}id_t type and associated helpers for v6.0. Convert the remaining places so we can remove all the old helpers. This is a non-functional change. Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- security/integrity/ima/ima_policy.c | 34 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) 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); From a03a972b26da2d30d1f3b3a72963191cd2938835 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 9 Sep 2022 11:40:21 +0200 Subject: [PATCH 6/8] fuse: port to vfs{g,u}id_t and associated helpers A while ago we introduced a dedicated vfs{g,u}id_t type in commit 1e5267cd0895 ("mnt_idmapping: add vfs{g,u}id_t"). We already switched over a good part of the VFS. Ultimately we will remove all legacy idmapped mount helpers that operate only on k{g,u}id_t in favor of the new type safe helpers that operate on vfs{g,u}id_t. Cc: Christoph Hellwig Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- fs/fuse/acl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c index 337cb29a8dd5..84c1ca4bc1dc 100644 --- a/fs/fuse/acl.c +++ b/fs/fuse/acl.c @@ -98,7 +98,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode, 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; From c12db92d62bf8cd4532ab4e572be0926d3a375be Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 9 Sep 2022 11:07:47 +0200 Subject: [PATCH 7/8] ovl: port to vfs{g,u}id_t and associated helpers A while ago we introduced a dedicated vfs{g,u}id_t type in commit 1e5267cd0895 ("mnt_idmapping: add vfs{g,u}id_t"). We already switched over a good part of the VFS. Ultimately we will remove all legacy idmapped mount helpers that operate only on k{g,u}id_t in favor of the new type safe helpers that operate on vfs{g,u}id_t. Cc: Amir Goldstein Cc: Christoph Hellwig Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- fs/overlayfs/util.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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; From eb7718cdb73c6b0c93002f8f73f4dd4701f8d2bb Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 29 Jun 2022 12:53:54 +0200 Subject: [PATCH 8/8] fs: remove unused idmapping helpers Now that all places can deal with the new type safe helpers remove all of the old helpers. Reviewed-by: Seth Forshee (DigitalOcean) Signed-off-by: Christian Brauner (Microsoft) --- include/linux/fs.h | 34 ------------------ include/linux/mnt_idmapping.h | 68 ----------------------------------- 2 files changed, 102 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index b39c5efca180..0951283f2515 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1612,23 +1612,6 @@ static inline void i_gid_write(struct inode *inode, gid_t gid) inode->i_gid = make_kgid(i_user_ns(inode), 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 @@ -1681,23 +1664,6 @@ static inline void i_uid_update(struct user_namespace *mnt_userns, attr->ia_vfsuid); } -/** - * 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 diff --git a/include/linux/mnt_idmapping.h b/include/linux/mnt_idmapping.h index cd1950ddc6a9..c8002294a72d 100644 --- a/include/linux/mnt_idmapping.h +++ b/include/linux/mnt_idmapping.h @@ -228,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 @@ -273,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 @@ -307,33 +293,6 @@ static inline kuid_t from_vfsuid(struct user_namespace *mnt_userns, return make_kuid(fs_userns, uid); } -/** - * 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 @@ -399,33 +358,6 @@ static inline kgid_t from_vfsgid(struct user_namespace *mnt_userns, return make_kgid(fs_userns, gid); } -/** - * 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