diff --git a/fs/attr.c b/fs/attr.c index b4bbdbd4c8ca096e86451b0443e64bc2ca9e2b1d..d270f640a192fbf3d6a05d7e1d646eb8ab98133c 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -23,7 +23,7 @@ static bool chown_ok(const struct inode *inode, kuid_t uid) if (uid_eq(current_fsuid(), inode->i_uid) && uid_eq(uid, inode->i_uid)) return true; - if (capable_wrt_inode_uidgid(inode, CAP_CHOWN)) + if (capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_CHOWN)) return true; if (uid_eq(inode->i_uid, INVALID_UID) && ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) @@ -36,7 +36,7 @@ static bool chgrp_ok(const struct inode *inode, kgid_t gid) if (uid_eq(current_fsuid(), inode->i_uid) && (in_group_p(gid) || gid_eq(gid, inode->i_gid))) return true; - if (capable_wrt_inode_uidgid(inode, CAP_CHOWN)) + if (capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_CHOWN)) return true; if (gid_eq(inode->i_gid, INVALID_GID) && ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) @@ -92,7 +92,7 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr) /* Also check the setgid bit! */ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : inode->i_gid) && - !capable_wrt_inode_uidgid(inode, CAP_FSETID)) + !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) attr->ia_mode &= ~S_ISGID; } @@ -193,7 +193,7 @@ void setattr_copy(struct inode *inode, const struct iattr *attr) umode_t mode = attr->ia_mode; if (!in_group_p(inode->i_gid) && - !capable_wrt_inode_uidgid(inode, CAP_FSETID)) + !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) mode &= ~S_ISGID; inode->i_mode = mode; } diff --git a/fs/exec.c b/fs/exec.c index 5d4d52039105cd3e017681c15f38324fd5681f4e..89d4780ff48f970b834c65b58e025013b3484539 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1411,7 +1411,8 @@ void would_dump(struct linux_binprm *bprm, struct file *file) /* Ensure mm->user_ns contains the executable */ user_ns = old = bprm->mm->user_ns; while ((user_ns != &init_user_ns) && - !privileged_wrt_inode_uidgid(user_ns, inode)) + !privileged_wrt_inode_uidgid(user_ns, &init_user_ns, + inode)) user_ns = user_ns->parent; if (old != user_ns) { diff --git a/fs/inode.c b/fs/inode.c index 6442d97d9a4ab37a82f55dda5801c5067ef9bf01..cd40cbf87ce4acab1f74210dcc3773374b2e779b 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2146,7 +2146,8 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, mode |= S_ISGID; else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) && !in_group_p(inode->i_gid) && - !capable_wrt_inode_uidgid(dir, CAP_FSETID)) + !capable_wrt_inode_uidgid(&init_user_ns, dir, + CAP_FSETID)) mode &= ~S_ISGID; } else inode->i_gid = current_fsgid(); diff --git a/fs/namei.c b/fs/namei.c index 78443a85480a5aea3b04b3064bb3ef594c83777b..fd4724bce4f5f11f5b3add79aed0c0c8c80899e4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -357,10 +357,11 @@ int generic_permission(struct inode *inode, int mask) if (S_ISDIR(inode->i_mode)) { /* DACs are overridable for directories */ if (!(mask & MAY_WRITE)) - if (capable_wrt_inode_uidgid(inode, + if (capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_DAC_READ_SEARCH)) return 0; - if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) + if (capable_wrt_inode_uidgid(&init_user_ns, inode, + CAP_DAC_OVERRIDE)) return 0; return -EACCES; } @@ -370,7 +371,8 @@ int generic_permission(struct inode *inode, int mask) */ mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (mask == MAY_READ) - if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH)) + if (capable_wrt_inode_uidgid(&init_user_ns, inode, + CAP_DAC_READ_SEARCH)) return 0; /* * Read/write DACs are always overridable. @@ -378,7 +380,8 @@ int generic_permission(struct inode *inode, int mask) * at least one exec bit set. */ if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO)) - if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) + if (capable_wrt_inode_uidgid(&init_user_ns, inode, + CAP_DAC_OVERRIDE)) return 0; return -EACCES; @@ -2659,7 +2662,7 @@ int __check_sticky(struct inode *dir, struct inode *inode) return 0; if (uid_eq(dir->i_uid, fsuid)) return 0; - return !capable_wrt_inode_uidgid(inode, CAP_FOWNER); + return !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FOWNER); } EXPORT_SYMBOL(__check_sticky); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 2bd570cbe8a4591f58610f9ede56e713b3eb2aa8..88d8777877700a60008d3200578f281547bcf30b 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1017,7 +1017,7 @@ ovl_posix_acl_xattr_set(const struct xattr_handler *handler, if (unlikely(inode->i_mode & S_ISGID) && handler->flags == ACL_TYPE_ACCESS && !in_group_p(inode->i_gid) && - !capable_wrt_inode_uidgid(inode, CAP_FSETID)) { + !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) { struct iattr iattr = { .ia_valid = ATTR_KILL_SGID }; err = ovl_setattr(dentry, &iattr); diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 95882b3f5f62f07380d8dbe242b0f89210e19f84..4ca6d53c6f0af0ac6ce300fdce252ae7d93ba225 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -656,7 +656,7 @@ int posix_acl_update_mode(struct inode *inode, umode_t *mode_p, if (error == 0) *acl = NULL; if (!in_group_p(inode->i_gid) && - !capable_wrt_inode_uidgid(inode, CAP_FSETID)) + !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) mode &= ~S_ISGID; *mode_p = mode; return 0; diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 3fbd98f61ea5c02ce200f6c38cc5e1c8f781f4f3..97bd29fc8c43fbc2189ec2dc5ae8cf8d75cad2ba 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -1502,7 +1502,7 @@ xfs_ioctl_setattr( */ if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) && - !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID)) + !capable_wrt_inode_uidgid(&init_user_ns, VFS_I(ip), CAP_FSETID)) VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID); /* Change the ownerships and register project quota modifications */ diff --git a/include/linux/capability.h b/include/linux/capability.h index b2f698915c0f3f41fbc13c49d0eeedc1c95c0af8..62bfa3ed84d78246cf2cc08b3d57d9226b8fb7df 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -247,8 +247,11 @@ static inline bool ns_capable_setid(struct user_namespace *ns, int cap) return true; } #endif /* CONFIG_MULTIUSER */ -extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); -extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); +bool privileged_wrt_inode_uidgid(struct user_namespace *ns, + struct user_namespace *mnt_userns, + const struct inode *inode); +bool capable_wrt_inode_uidgid(struct user_namespace *mnt_userns, + const struct inode *inode, int cap); extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns); static inline bool perfmon_capable(void) diff --git a/kernel/capability.c b/kernel/capability.c index de7eac903a2a5f6e6af42158b753f711dff9093c..46a361dde0421a29e7fdd25386c9e0f1f6e6775a 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -484,10 +484,12 @@ EXPORT_SYMBOL(file_ns_capable); * * Return true if the inode uid and gid are within the namespace. */ -bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode) +bool privileged_wrt_inode_uidgid(struct user_namespace *ns, + struct user_namespace *mnt_userns, + const struct inode *inode) { - return kuid_has_mapping(ns, inode->i_uid) && - kgid_has_mapping(ns, inode->i_gid); + return kuid_has_mapping(ns, i_uid_into_mnt(mnt_userns, inode)) && + kgid_has_mapping(ns, i_gid_into_mnt(mnt_userns, inode)); } /** @@ -499,11 +501,13 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode * * its own user namespace and that the given inode's uid and gid are * mapped into the current user namespace. */ -bool capable_wrt_inode_uidgid(const struct inode *inode, int cap) +bool capable_wrt_inode_uidgid(struct user_namespace *mnt_userns, + const struct inode *inode, int cap) { struct user_namespace *ns = current_user_ns(); - return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode); + return ns_capable(ns, cap) && + privileged_wrt_inode_uidgid(ns, mnt_userns, inode); } EXPORT_SYMBOL(capable_wrt_inode_uidgid); diff --git a/security/commoncap.c b/security/commoncap.c index bacc1111d871bc257c5f86bbf206430f64cb8d79..88ee345f7bfa4e49176c4837e3a03ac9e7002168 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -489,7 +489,7 @@ int cap_convert_nscap(struct dentry *dentry, const void **ivalue, size_t size) return -EINVAL; if (!validheader(size, cap)) return -EINVAL; - if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP)) + if (!capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_SETFCAP)) return -EPERM; if (size == XATTR_CAPS_SZ_2) if (ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP)) @@ -956,7 +956,8 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) struct inode *inode = d_backing_inode(dentry); if (!inode) return -EINVAL; - if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP)) + if (!capable_wrt_inode_uidgid(&init_user_ns, inode, + CAP_SETFCAP)) return -EPERM; return 0; }