Commit d381d8a9 authored by James Morris's avatar James Morris Committed by Linus Torvalds

[PATCH] SELinux: canonicalize getxattr()

This patch allows SELinux to canonicalize the value returned from
getxattr() via the security_inode_getsecurity() hook, which is called after
the fs level getxattr() function.

The purpose of this is to allow the in-core security context for an inode
to override the on-disk value.  This could happen in cases such as
upgrading a system to a different labeling form (e.g.  standard SELinux to
MLS) without needing to do a full relabel of the filesystem.

In such cases, we want getxattr() to return the canonical security context
that the kernel is using rather than what is stored on disk.

The implementation hooks into the inode_getsecurity(), adding another
parameter to indicate the result of the preceding fs-level getxattr() call,
so that SELinux knows whether to compare a value obtained from disk with
the kernel value.

We also now allow getxattr() to work for mountpoint labeled filesystems
(i.e.  mount with option context=foo_t), as we are able to return the
kernel value to the user.
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
Signed-off-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 89d155ef
...@@ -143,7 +143,7 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) ...@@ -143,7 +143,7 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size)
if (size) { if (size) {
if (size > XATTR_SIZE_MAX) if (size > XATTR_SIZE_MAX)
size = XATTR_SIZE_MAX; size = XATTR_SIZE_MAX;
kvalue = kmalloc(size, GFP_KERNEL); kvalue = kzalloc(size, GFP_KERNEL);
if (!kvalue) if (!kvalue)
return -ENOMEM; return -ENOMEM;
} }
...@@ -154,11 +154,15 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) ...@@ -154,11 +154,15 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size)
error = -EOPNOTSUPP; error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->getxattr) if (d->d_inode->i_op && d->d_inode->i_op->getxattr)
error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); 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; const char *suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1;
error = security_inode_getsecurity(d->d_inode, suffix, kvalue, int rv = security_inode_getsecurity(d->d_inode, suffix, kvalue,
size); size, error);
/* Security module active: overwrite error value */
if (rv != -EOPNOTSUPP)
error = rv;
} }
if (error > 0) { if (error > 0) {
if (size && copy_to_user(value, kvalue, error)) if (size && copy_to_user(value, kvalue, error))
......
...@@ -385,6 +385,9 @@ struct swap_info_struct; ...@@ -385,6 +385,9 @@ struct swap_info_struct;
* NULL to request the size of the buffer required. @size indicates * NULL to request the size of the buffer required. @size indicates
* the size of @buffer in bytes. Note that @name is the remainder * the size of @buffer in bytes. Note that @name is the remainder
* of the attribute name after the security. prefix has been removed. * 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. * Return number of bytes used/required on success.
* @inode_setsecurity: * @inode_setsecurity:
* Set the security label associated with @name for @inode from the * Set the security label associated with @name for @inode from the
...@@ -1091,7 +1094,7 @@ struct security_operations { ...@@ -1091,7 +1094,7 @@ struct security_operations {
int (*inode_getxattr) (struct dentry *dentry, char *name); int (*inode_getxattr) (struct dentry *dentry, char *name);
int (*inode_listxattr) (struct dentry *dentry); int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, char *name); 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_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); 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) ...@@ -1580,11 +1583,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return security_ops->inode_removexattr (dentry, 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))) if (unlikely (IS_PRIVATE (inode)))
return 0; 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) 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) ...@@ -2222,7 +2225,7 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return cap_inode_removexattr(dentry, 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; return -EOPNOTSUPP;
} }
......
...@@ -377,7 +377,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name) ...@@ -377,7 +377,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name)
return 0; 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; return -EOPNOTSUPP;
} }
......
...@@ -2208,9 +2208,6 @@ static int selinux_inode_getxattr (struct dentry *dentry, char *name) ...@@ -2208,9 +2208,6 @@ static int selinux_inode_getxattr (struct dentry *dentry, char *name)
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct superblock_security_struct *sbsec = inode->i_sb->s_security; 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); return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
} }
...@@ -2241,33 +2238,54 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) ...@@ -2241,33 +2238,54 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name)
return -EACCES; 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; struct inode_security_struct *isec = inode->i_security;
char *context; char *context;
unsigned len; unsigned len;
int rc; int rc;
/* Permission check handled by selinux_inode_getxattr hook.*/ if (strcmp(name, XATTR_SELINUX_SUFFIX)) {
rc = -EOPNOTSUPP;
if (strcmp(name, XATTR_SELINUX_SUFFIX)) goto out;
return -EOPNOTSUPP; }
rc = security_sid_to_context(isec->sid, &context, &len); rc = security_sid_to_context(isec->sid, &context, &len);
if (rc) if (rc)
return rc; goto out;
/* Probe for required buffer size */
if (!buffer || !size) { if (!buffer || !size) {
kfree(context); rc = len;
return len; goto out_free;
} }
if (size < len) { if (size < len) {
kfree(context); rc = -ERANGE;
return -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); memcpy(buffer, context, len);
rc = len;
out_free:
kfree(context); kfree(context);
return len; out:
return rc;
} }
static int selinux_inode_setsecurity(struct inode *inode, const char *name, static int selinux_inode_setsecurity(struct inode *inode, const char *name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment