From: Andreas Gruenbacher Subject: Pass struct file down the inode_*xattr security LSM hooks This allows LSMs to also distinguish between file descriptor and path access for the xattr operations. (The other relevant operations are covered by the setattr hook.) Signed-off-by: Andreas Gruenbacher Signed-off-by: John Johansen --- fs/xattr.c | 61 +++++++++++++++++++++++++---------------------- include/linux/security.h | 38 ++++++++++++++++------------- include/linux/xattr.h | 8 +++--- security/commoncap.c | 4 +-- security/dummy.c | 9 +++--- security/security.c | 17 ++++++------- security/selinux/hooks.c | 10 ++++--- 7 files changed, 81 insertions(+), 66 deletions(-) --- a/fs/xattr.c +++ b/fs/xattr.c @@ -68,7 +68,7 @@ xattr_permission(struct inode *inode, co int vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, - const void *value, size_t size, int flags) + const void *value, size_t size, int flags, struct file *file) { struct inode *inode = dentry->d_inode; int error; @@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, stru return error; mutex_lock(&inode->i_mutex); - error = security_inode_setxattr(dentry, mnt, name, value, size, flags); + error = security_inode_setxattr(dentry, mnt, name, value, size, flags, file); if (error) goto out; error = -EOPNOTSUPP; @@ -132,7 +132,7 @@ EXPORT_SYMBOL_GPL(xattr_getsecurity); ssize_t vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, - void *value, size_t size) + void *value, size_t size, struct file *file) { struct inode *inode = dentry->d_inode; int error; @@ -141,7 +141,7 @@ vfs_getxattr(struct dentry *dentry, stru if (error) return error; - error = security_inode_getxattr(dentry, mnt, name); + error = security_inode_getxattr(dentry, mnt, name, file); if (error) return error; @@ -169,12 +169,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr); ssize_t vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list, - size_t size) + size_t size, struct file *file) { struct inode *inode = dentry->d_inode; ssize_t error; - error = security_inode_listxattr(dentry, mnt); + error = security_inode_listxattr(dentry, mnt, file); if (error) return error; error = -EOPNOTSUPP; @@ -190,7 +190,8 @@ vfs_listxattr(struct dentry *dentry, str EXPORT_SYMBOL_GPL(vfs_listxattr); int -vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name) +vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, + struct file *file) { struct inode *inode = dentry->d_inode; int error; @@ -202,7 +203,7 @@ vfs_removexattr(struct dentry *dentry, s if (error) return error; - error = security_inode_removexattr(dentry, mnt, name); + error = security_inode_removexattr(dentry, mnt, name, file); if (error) return error; @@ -222,7 +223,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr); */ static long setxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name, - const void __user *value, size_t size, int flags) + const void __user *value, size_t size, int flags, struct file *file) { int error; void *kvalue = NULL; @@ -249,7 +250,7 @@ setxattr(struct dentry *dentry, struct v } } - error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags); + error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags, file); kfree(kvalue); return error; } @@ -266,7 +267,8 @@ sys_setxattr(const char __user *path, co return error; error = mnt_want_write(nd.path.mnt); if (!error) { - error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); + error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, + flags, NULL); mnt_drop_write(nd.path.mnt); } path_put(&nd.path); @@ -285,7 +287,8 @@ sys_lsetxattr(const char __user *path, c return error; error = mnt_want_write(nd.path.mnt); if (!error) { - error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); + error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, + flags, NULL); mnt_drop_write(nd.path.mnt); } path_put(&nd.path); @@ -307,7 +310,8 @@ sys_fsetxattr(int fd, const char __user audit_inode(NULL, dentry); error = mnt_want_write(f->f_path.mnt); if (!error) { - error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags); + error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags, + f); mnt_drop_write(f->f_path.mnt); } fput(f); @@ -319,7 +323,7 @@ sys_fsetxattr(int fd, const char __user */ static ssize_t getxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name, - void __user *value, size_t size) + void __user *value, size_t size, struct file *file) { ssize_t error; void *kvalue = NULL; @@ -339,7 +343,7 @@ getxattr(struct dentry *dentry, struct v return -ENOMEM; } - error = vfs_getxattr(dentry, mnt, kname, kvalue, size); + error = vfs_getxattr(dentry, mnt, kname, kvalue, size, file); if (error > 0) { if (size && copy_to_user(value, kvalue, error)) error = -EFAULT; @@ -362,7 +366,7 @@ sys_getxattr(const char __user *path, co error = user_path_walk(path, &nd); if (error) return error; - error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size); + error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size, NULL); path_put(&nd.path); return error; } @@ -377,7 +381,7 @@ sys_lgetxattr(const char __user *path, c error = user_path_walk_link(path, &nd); if (error) return error; - error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size); + error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size, NULL); path_put(&nd.path); return error; } @@ -392,7 +396,7 @@ sys_fgetxattr(int fd, const char __user if (!f) return error; audit_inode(NULL, f->f_path.dentry); - error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size); + error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size, f); fput(f); return error; } @@ -402,7 +406,7 @@ sys_fgetxattr(int fd, const char __user */ static ssize_t listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list, - size_t size) + size_t size, struct file *file) { ssize_t error; char *klist = NULL; @@ -415,7 +419,7 @@ listxattr(struct dentry *dentry, struct return -ENOMEM; } - error = vfs_listxattr(dentry, mnt, klist, size); + error = vfs_listxattr(dentry, mnt, klist, size, file); if (error > 0) { if (size && copy_to_user(list, klist, error)) error = -EFAULT; @@ -437,7 +441,7 @@ sys_listxattr(const char __user *path, c error = user_path_walk(path, &nd); if (error) return error; - error = listxattr(nd.path.dentry, nd.path.mnt, list, size); + error = listxattr(nd.path.dentry, nd.path.mnt, list, size, NULL); path_put(&nd.path); return error; } @@ -451,7 +455,7 @@ sys_llistxattr(const char __user *path, error = user_path_walk_link(path, &nd); if (error) return error; - error = listxattr(nd.path.dentry, nd.path.mnt, list, size); + error = listxattr(nd.path.dentry, nd.path.mnt, list, size, NULL); path_put(&nd.path); return error; } @@ -466,7 +470,7 @@ sys_flistxattr(int fd, char __user *list if (!f) return error; audit_inode(NULL, f->f_path.dentry); - error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size); + error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size, f); fput(f); return error; } @@ -475,7 +479,8 @@ sys_flistxattr(int fd, char __user *list * Extended attribute REMOVE operations */ static long -removexattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name) +removexattr(struct dentry *dentry, struct vfsmount *mnt, + const char __user *name, struct file *file) { int error; char kname[XATTR_NAME_MAX + 1]; @@ -486,7 +491,7 @@ removexattr(struct dentry *dentry, struc if (error < 0) return error; - return vfs_removexattr(dentry, mnt, kname); + return vfs_removexattr(dentry, mnt, kname, file); } asmlinkage long @@ -500,7 +505,7 @@ sys_removexattr(const char __user *path, return error; error = mnt_want_write(nd.path.mnt); if (!error) { - error = removexattr(nd.path.dentry, nd.path.mnt, name); + error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL); mnt_drop_write(nd.path.mnt); } path_put(&nd.path); @@ -518,7 +523,7 @@ sys_lremovexattr(const char __user *path return error; error = mnt_want_write(nd.path.mnt); if (!error) { - error = removexattr(nd.path.dentry, nd.path.mnt, name); + error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL); mnt_drop_write(nd.path.mnt); } path_put(&nd.path); @@ -539,7 +544,7 @@ sys_fremovexattr(int fd, const char __us audit_inode(NULL, dentry); error = mnt_want_write(f->f_path.mnt); if (!error) { - error = removexattr(dentry, f->f_path.mnt, name); + error = removexattr(dentry, f->f_path.mnt, name, f); mnt_drop_write(f->f_path.mnt); } fput(f); --- a/include/linux/security.h +++ b/include/linux/security.h @@ -55,9 +55,9 @@ extern void cap_bprm_apply_creds(struct extern int cap_bprm_secureexec(struct linux_binprm *bprm); extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, size_t size, - int flags); + int flags, struct file *file); extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name); + const char *name, struct file *file); extern int cap_inode_need_killpriv(struct dentry *dentry); extern int cap_inode_killpriv(struct dentry *dentry); extern int cap_task_post_setuid(uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags); @@ -1397,16 +1397,17 @@ struct security_operations { void (*inode_delete) (struct inode *inode); int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, size_t size, - int flags); + int flags, struct file *file); void (*inode_post_setxattr) (struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, size_t size, int flags); int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, - const char *name); - int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); + const char *name, struct file *file); + int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt, + struct file *file); int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt, - const char *name); + const char *name, struct file *file); int (*inode_need_killpriv) (struct dentry *dentry); int (*inode_killpriv) (struct dentry *dentry); int (*inode_getsecurity) (const struct inode *inode, const char *name, void **buffer, bool alloc); @@ -1681,15 +1682,16 @@ int security_inode_getattr(struct vfsmou void security_inode_delete(struct inode *inode); int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, - size_t size, int flags); + size_t size, int flags, struct file *file); void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, size_t size, int flags); int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name); -int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt); + const char *name, struct file *file); +int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt, + struct file *file); int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name); + const char *name, struct file *file); int security_inode_need_killpriv(struct dentry *dentry); int security_inode_killpriv(struct dentry *dentry); int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc); @@ -2106,9 +2108,10 @@ static inline void security_inode_delete static inline int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, - size_t size, int flags) + size_t size, int flags, + struct file *file) { - return cap_inode_setxattr(dentry, mnt, name, value, size, flags); + return cap_inode_setxattr(dentry, mnt, name, value, size, flags, file); } static inline void security_inode_post_setxattr(struct dentry *dentry, @@ -2120,22 +2123,25 @@ static inline void security_inode_post_s static inline int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, + struct file *file) { return 0; } static inline int security_inode_listxattr(struct dentry *dentry, - struct vfsmount *mnt) + struct vfsmount *mnt, + struct file *file) { return 0; } static inline int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, + struct file *file) { - return cap_inode_removexattr(dentry, mnt, name); + return cap_inode_removexattr(dentry, mnt, name, file); } static inline int security_inode_need_killpriv(struct dentry *dentry) --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -47,10 +47,10 @@ struct xattr_handler { }; ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); -ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t); -ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size); -int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int); -int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *); +ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t, struct file *file); +ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size, struct file *file); +int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int, struct file *file); +int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *, struct file *file); ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size); ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); --- a/security/commoncap.c +++ b/security/commoncap.c @@ -385,7 +385,7 @@ int cap_bprm_secureexec (struct linux_bi int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, size_t size, - int flags) + int flags, struct file *file) { if (!strcmp(name, XATTR_NAME_CAPS)) { if (!capable(CAP_SETFCAP)) @@ -399,7 +399,7 @@ int cap_inode_setxattr(struct dentry *de } int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, struct file *file) { if (!strcmp(name, XATTR_NAME_CAPS)) { if (!capable(CAP_SETFCAP)) --- a/security/dummy.c +++ b/security/dummy.c @@ -376,7 +376,7 @@ static void dummy_inode_delete (struct i static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, - size_t size, int flags) + size_t size, int flags, struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) && @@ -393,18 +393,19 @@ static void dummy_inode_post_setxattr (s } static int dummy_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, struct file *file) { return 0; } -static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt) +static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt, + struct file *file) { return 0; } static int dummy_inode_removexattr (struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) && --- a/security/security.c +++ b/security/security.c @@ -502,12 +502,12 @@ void security_inode_delete(struct inode int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, size_t size, - int flags) + int flags, struct file *file) { if (unlikely(IS_PRIVATE(dentry->d_inode))) return 0; return security_ops->inode_setxattr(dentry, mnt, name, value, size, - flags); + flags, file); } void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, @@ -521,26 +521,27 @@ void security_inode_post_setxattr(struct } int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, struct file *file) { if (unlikely(IS_PRIVATE(dentry->d_inode))) return 0; - return security_ops->inode_getxattr(dentry, mnt, name); + return security_ops->inode_getxattr(dentry, mnt, name, file); } -int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt) +int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt, + struct file *file) { if (unlikely(IS_PRIVATE(dentry->d_inode))) return 0; - return security_ops->inode_listxattr(dentry, mnt); + return security_ops->inode_listxattr(dentry, mnt, file); } int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, struct file *file) { if (unlikely(IS_PRIVATE(dentry->d_inode))) return 0; - return security_ops->inode_removexattr(dentry, mnt, name); + return security_ops->inode_removexattr(dentry, mnt, name, file); } int security_inode_need_killpriv(struct dentry *dentry) --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2657,7 +2657,7 @@ static int selinux_inode_setotherxattr(s static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, const void *value, - size_t size, int flags) + size_t size, int flags, struct file *file) { struct task_security_struct *tsec = current->security; struct inode *inode = dentry->d_inode; @@ -2733,18 +2733,20 @@ static void selinux_inode_post_setxattr( } static int selinux_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, - const char *name) + const char *name, struct file *file) { return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); } -static int selinux_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt) +static int selinux_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt, + struct file *file) { return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); } static int selinux_inode_removexattr(struct dentry *dentry, - struct vfsmount *mnt, const char *name) + struct vfsmount *mnt, const char *name, + struct file *file) { if (strcmp(name, XATTR_NAME_SELINUX)) return selinux_inode_setotherxattr(dentry, name);