*/
#include <linux/fs.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/file.h>
#include <linux/xattr.h>
#include <linux/namei.h>
#include <linux/syscalls.h>
#include <linux/module.h>
#include <linux/fsnotify.h>
+#include <linux/audit.h>
#include <asm/uaccess.h>
+/*
+ * 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) && !is_owner_or_cap(inode))
+ return -EPERM;
+ }
+
+ return permission(inode, mask, NULL);
+}
+
int
vfs_setxattr(struct dentry *dentry, char *name, void *value,
size_t size, int flags)
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)
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)
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;
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);
/*
}
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)
{
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;
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);
+ error = setxattr(dentry, name, value, size, flags);
fput(f);
return error;
}
f = fget(fd);
if (!f)
return error;
- error = getxattr(f->f_dentry, name, value, size);
+ audit_inode(NULL, f->f_path.dentry);
+ error = getxattr(f->f_path.dentry, name, value, size);
fput(f);
return error;
}
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;
than XATTR_LIST_MAX bytes. Not possible. */
error = -E2BIG;
}
-out:
kfree(klist);
return error;
}
f = fget(fd);
if (!f)
return error;
- error = listxattr(f->f_dentry, list, size);
+ audit_inode(NULL, f->f_path.dentry);
+ error = listxattr(f->f_path.dentry, list, size);
fput(f);
return error;
}
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);
+ error = removexattr(dentry, name);
fput(f);
return error;
}