X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fxattr.c;h=4523aca79659c24f5d2ca3b8ee415ddd9588b73f;hb=2d3bb25209c1f9a27ea9535c7fd2f6729a5e7db1;hp=fee804e69a9a40206fd55ca81dc3c31ace876eca;hpb=5be196e5f925dab2309530fabce69c2e562b9791;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/xattr.c b/fs/xattr.c index fee804e..4523aca 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -9,7 +9,6 @@ */ #include #include -#include #include #include #include @@ -17,9 +16,58 @@ #include #include #include +#include #include +/* + * Check permissions for extended attribute access. This is a bit complicated + * because different namespaces have very different rules. + */ +static int +xattr_permission(struct inode *inode, const char *name, int mask) +{ + /* + * We can never set or remove an extended attribute on a read-only + * filesystem or on an immutable / append-only inode. + */ + if (mask & MAY_WRITE) { + if (IS_RDONLY(inode)) + return -EROFS; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EPERM; + } + + /* + * No restriction for security.* and system.* from the VFS. Decision + * on these is left to the underlying filesystem / security module. + */ + if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) || + !strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) + return 0; + + /* + * The trusted.* namespace can only be accessed by a privileged user. + */ + if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) + return (capable(CAP_SYS_ADMIN) ? 0 : -EPERM); + + /* In user.* namespace, only regular files and directories can have + * extended attributes. For sticky directories, only the owner and + * privileged user can write attributes. + */ + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) { + if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) + return -EPERM; + if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) && + (mask & MAY_WRITE) && (current->fsuid != inode->i_uid) && + !capable(CAP_FOWNER)) + return -EPERM; + } + + return permission(inode, mask, NULL); +} + int vfs_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags) @@ -27,6 +75,10 @@ vfs_setxattr(struct dentry *dentry, char *name, void *value, struct inode *inode = dentry->d_inode; int error; + error = xattr_permission(inode, name, MAY_WRITE); + if (error) + return error; + mutex_lock(&inode->i_mutex); error = security_inode_setxattr(dentry, name, value, size, flags); if (error) @@ -40,8 +92,8 @@ vfs_setxattr(struct dentry *dentry, char *name, void *value, size, flags); } } else if (!strncmp(name, XATTR_SECURITY_PREFIX, - sizeof XATTR_SECURITY_PREFIX - 1)) { - const char *suffix = name + sizeof XATTR_SECURITY_PREFIX - 1; + XATTR_SECURITY_PREFIX_LEN)) { + const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; error = security_inode_setsecurity(inode, suffix, value, size, flags); if (!error) @@ -59,6 +111,10 @@ vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) struct inode *inode = dentry->d_inode; int error; + error = xattr_permission(inode, name, MAY_READ); + if (error) + return error; + error = security_inode_getxattr(dentry, name); if (error) return error; @@ -69,8 +125,8 @@ vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) error = -EOPNOTSUPP; if (!strncmp(name, XATTR_SECURITY_PREFIX, - sizeof XATTR_SECURITY_PREFIX - 1)) { - const char *suffix = name + sizeof XATTR_SECURITY_PREFIX - 1; + XATTR_SECURITY_PREFIX_LEN)) { + const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; int ret = security_inode_getsecurity(inode, suffix, value, size, error); /* @@ -85,6 +141,26 @@ vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) } EXPORT_SYMBOL_GPL(vfs_getxattr); +ssize_t +vfs_listxattr(struct dentry *d, char *list, size_t size) +{ + ssize_t error; + + error = security_inode_listxattr(d); + if (error) + return error; + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { + error = d->d_inode->i_op->listxattr(d, list, size); + } else { + error = security_inode_listsecurity(d->d_inode, list, size); + if (size && error > size) + error = -ERANGE; + } + return error; +} +EXPORT_SYMBOL_GPL(vfs_listxattr); + int vfs_removexattr(struct dentry *dentry, char *name) { @@ -94,6 +170,10 @@ vfs_removexattr(struct dentry *dentry, char *name) if (!inode->i_op->removexattr) return -EOPNOTSUPP; + error = xattr_permission(inode, name, MAY_WRITE); + if (error) + return error; + error = security_inode_removexattr(dentry, name); if (error) return error; @@ -181,12 +261,15 @@ sys_fsetxattr(int fd, char __user *name, void __user *value, size_t size, int flags) { struct file *f; + struct dentry *dentry; int error = -EBADF; f = fget(fd); if (!f) return error; - error = setxattr(f->f_dentry, name, value, size, flags); + dentry = f->f_path.dentry; + audit_inode(NULL, dentry->d_inode); + error = setxattr(dentry, name, value, size, flags); fput(f); return error; } @@ -267,7 +350,8 @@ sys_fgetxattr(int fd, char __user *name, void __user *value, size_t size) f = fget(fd); if (!f) return error; - error = getxattr(f->f_dentry, name, value, size); + audit_inode(NULL, f->f_path.dentry->d_inode); + error = getxattr(f->f_path.dentry, name, value, size); fput(f); return error; } @@ -289,17 +373,7 @@ listxattr(struct dentry *d, char __user *list, size_t size) return -ENOMEM; } - error = security_inode_listxattr(d); - if (error) - goto out; - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { - error = d->d_inode->i_op->listxattr(d, klist, size); - } else { - error = security_inode_listsecurity(d->d_inode, klist, size); - if (size && error > size) - error = -ERANGE; - } + error = vfs_listxattr(d, klist, size); if (error > 0) { if (size && copy_to_user(list, klist, error)) error = -EFAULT; @@ -308,7 +382,6 @@ listxattr(struct dentry *d, char __user *list, size_t size) than XATTR_LIST_MAX bytes. Not possible. */ error = -E2BIG; } -out: kfree(klist); return error; } @@ -350,7 +423,8 @@ sys_flistxattr(int fd, char __user *list, size_t size) f = fget(fd); if (!f) return error; - error = listxattr(f->f_dentry, list, size); + audit_inode(NULL, f->f_path.dentry->d_inode); + error = listxattr(f->f_path.dentry, list, size); fput(f); return error; } @@ -405,12 +479,15 @@ asmlinkage long sys_fremovexattr(int fd, char __user *name) { struct file *f; + struct dentry *dentry; int error = -EBADF; f = fget(fd); if (!f) return error; - error = removexattr(f->f_dentry, name); + dentry = f->f_path.dentry; + audit_inode(NULL, dentry->d_inode); + error = removexattr(dentry, name); fput(f); return error; }