summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/xattr.c14
-rw-r--r--include/linux/security.h11
-rw-r--r--security/dummy.c2
-rw-r--r--security/selinux/hooks.c46
4 files changed, 49 insertions, 24 deletions
diff --git a/fs/xattr.c b/fs/xattr.c
index 3f9c64bea151..f6e00c0e114f 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -143,7 +143,7 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size)
if (size) {
if (size > XATTR_SIZE_MAX)
size = XATTR_SIZE_MAX;
- kvalue = kmalloc(size, GFP_KERNEL);
+ kvalue = kzalloc(size, GFP_KERNEL);
if (!kvalue)
return -ENOMEM;
}
@@ -154,11 +154,15 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size)
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->getxattr)
error = d->d_inode->i_op->getxattr(d, kname, kvalue, size);
- else if (!strncmp(kname, XATTR_SECURITY_PREFIX,
- sizeof XATTR_SECURITY_PREFIX - 1)) {
+
+ if (!strncmp(kname, XATTR_SECURITY_PREFIX,
+ sizeof XATTR_SECURITY_PREFIX - 1)) {
const char *suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1;
- error = security_inode_getsecurity(d->d_inode, suffix, kvalue,
- size);
+ int rv = security_inode_getsecurity(d->d_inode, suffix, kvalue,
+ size, error);
+ /* Security module active: overwrite error value */
+ if (rv != -EOPNOTSUPP)
+ error = rv;
}
if (error > 0) {
if (size && copy_to_user(value, kvalue, error))
diff --git a/include/linux/security.h b/include/linux/security.h
index dac956ed98f0..607ee209ea3b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -385,6 +385,9 @@ struct swap_info_struct;
* NULL to request the size of the buffer required. @size indicates
* the size of @buffer in bytes. Note that @name is the remainder
* of the attribute name after the security. prefix has been removed.
+ * @err is the return value from the preceding fs getxattr call,
+ * and can be used by the security module to determine whether it
+ * should try and canonicalize the attribute value.
* Return number of bytes used/required on success.
* @inode_setsecurity:
* Set the security label associated with @name for @inode from the
@@ -1091,7 +1094,7 @@ struct security_operations {
int (*inode_getxattr) (struct dentry *dentry, char *name);
int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, char *name);
- int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size);
+ int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err);
int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
@@ -1580,11 +1583,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return security_ops->inode_removexattr (dentry, name);
}
-static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
+static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
if (unlikely (IS_PRIVATE (inode)))
return 0;
- return security_ops->inode_getsecurity(inode, name, buffer, size);
+ return security_ops->inode_getsecurity(inode, name, buffer, size, err);
}
static inline int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
@@ -2222,7 +2225,7 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return cap_inode_removexattr(dentry, name);
}
-static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
+static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
return -EOPNOTSUPP;
}
diff --git a/security/dummy.c b/security/dummy.c
index 3d34f3de7e82..2a0337a52d32 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -377,7 +377,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name)
return 0;
}
-static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
+static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
return -EOPNOTSUPP;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8cd33b2cd865..d9ec85292e1c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2208,9 +2208,6 @@ static int selinux_inode_getxattr (struct dentry *dentry, char *name)
struct inode *inode = dentry->d_inode;
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
- return -EOPNOTSUPP;
-
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
@@ -2241,33 +2238,54 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name)
return -EACCES;
}
-static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
+/*
+ * Copy the in-core inode security context value to the user. If the
+ * getxattr() prior to this succeeded, check to see if we need to
+ * canonicalize the value to be finally returned to the user.
+ *
+ * Permission check is handled by selinux_inode_getxattr hook.
+ */
+static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
struct inode_security_struct *isec = inode->i_security;
char *context;
unsigned len;
int rc;
- /* Permission check handled by selinux_inode_getxattr hook.*/
-
- if (strcmp(name, XATTR_SELINUX_SUFFIX))
- return -EOPNOTSUPP;
+ if (strcmp(name, XATTR_SELINUX_SUFFIX)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
rc = security_sid_to_context(isec->sid, &context, &len);
if (rc)
- return rc;
+ goto out;
+ /* Probe for required buffer size */
if (!buffer || !size) {
- kfree(context);
- return len;
+ rc = len;
+ goto out_free;
}
+
if (size < len) {
- kfree(context);
- return -ERANGE;
+ rc = -ERANGE;
+ goto out_free;
+ }
+
+ if (err > 0) {
+ if ((len == err) && !(memcmp(context, buffer, len))) {
+ /* Don't need to canonicalize value */
+ rc = err;
+ goto out_free;
+ }
+ memset(buffer, 0, size);
}
memcpy(buffer, context, len);
+ rc = len;
+out_free:
kfree(context);
- return len;
+out:
+ return rc;
}
static int selinux_inode_setsecurity(struct inode *inode, const char *name,