Commit 4a3fbc84 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] devpts xattr handler for security labels

From: Stephen Smalley <sds@epoch.ncsc.mil>

This patch against 2.5.69-bk adds an xattr handler for security labels
to devpts and corresponding hooks to the LSM API to support conversion
between xattr values and the security labels stored in the inode
security field by the security module.

This allows userspace to get and set the security labels on devpts
nodes, e.g.  so that sshd can set the security label for the pty using
setxattr, just as sshd already sets the ownership using chown.

SELinux uses this support to protect the pty in accordance with the user
process' security label.  The changes to the LSM API are general and
should be re-useable by xattr handlers in other pseudo filesystems to
support similar security labeling.  The xattr handler for devpts
includes the same generic framework as in ext[23], so handlers for other
kinds of attributes can be added easily in the future.
parent fb39f360
...@@ -827,6 +827,28 @@ config DEVPTS_FS ...@@ -827,6 +827,28 @@ config DEVPTS_FS
Note that the experimental "/dev file system support" Note that the experimental "/dev file system support"
(CONFIG_DEVFS_FS) is a more general facility. (CONFIG_DEVFS_FS) is a more general facility.
config DEVPTS_FS_XATTR
bool "/dev/pts Extended Attributes"
depends on DEVPTS_FS
help
Extended attributes are name:value pairs associated with inodes by
the kernel or by users (see the attr(5) manual page, or visit
<http://acl.bestbits.at/> for details).
If unsure, say N.
config DEVPTS_FS_SECURITY
bool "/dev/pts Security Labels"
depends on DEVPTS_FS_XATTR
help
Security labels support alternative access control models
implemented by security modules like SELinux. This option
enables an extended attribute handler for file security
labels in the /dev/pts filesystem.
If you are not using a security module that requires using
extended attributes for file security labels, say N.
config TMPFS config TMPFS
bool "Virtual memory file system support (former shm fs)" bool "Virtual memory file system support (former shm fs)"
help help
......
...@@ -5,3 +5,11 @@ ...@@ -5,3 +5,11 @@
obj-$(CONFIG_DEVPTS_FS) += devpts.o obj-$(CONFIG_DEVPTS_FS) += devpts.o
devpts-objs := inode.o devpts-objs := inode.o
ifeq ($(CONFIG_DEVPTS_FS_XATTR),y)
devpts-objs += xattr.o
endif
ifeq ($(CONFIG_DEVPTS_FS_SECURITY),y)
devpts-objs += xattr_security.o
endif
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mount.h> #include <linux/mount.h>
#include "xattr.h"
#define DEVPTS_SUPER_MAGIC 0x1cd1 #define DEVPTS_SUPER_MAGIC 0x1cd1
...@@ -130,6 +131,13 @@ static struct dentry *get_node(int num) ...@@ -130,6 +131,13 @@ static struct dentry *get_node(int num)
return lookup_one_len(s, root, sprintf(s, "%d", num)); return lookup_one_len(s, root, sprintf(s, "%d", num));
} }
static struct inode_operations devpts_file_inode_operations = {
.setxattr = devpts_setxattr,
.getxattr = devpts_getxattr,
.listxattr = devpts_listxattr,
.removexattr = devpts_removexattr,
};
void devpts_pty_new(int number, dev_t device) void devpts_pty_new(int number, dev_t device)
{ {
struct dentry *dentry; struct dentry *dentry;
...@@ -142,6 +150,7 @@ void devpts_pty_new(int number, dev_t device) ...@@ -142,6 +150,7 @@ void devpts_pty_new(int number, dev_t device)
inode->i_gid = config.setgid ? config.gid : current->fsgid; inode->i_gid = config.setgid ? config.gid : current->fsgid;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device); init_special_inode(inode, S_IFCHR|config.mode, device);
inode->i_op = &devpts_file_inode_operations;
dentry = get_node(number); dentry = get_node(number);
if (!IS_ERR(dentry) && !dentry->d_inode) if (!IS_ERR(dentry) && !dentry->d_inode)
...@@ -167,7 +176,10 @@ void devpts_pty_kill(int number) ...@@ -167,7 +176,10 @@ void devpts_pty_kill(int number)
static int __init init_devpts_fs(void) static int __init init_devpts_fs(void)
{ {
int err = register_filesystem(&devpts_fs_type); int err = init_devpts_xattr();
if (err)
return err;
err = register_filesystem(&devpts_fs_type);
if (!err) { if (!err) {
devpts_mnt = kern_mount(&devpts_fs_type); devpts_mnt = kern_mount(&devpts_fs_type);
err = PTR_ERR(devpts_mnt); err = PTR_ERR(devpts_mnt);
...@@ -181,6 +193,7 @@ static void __exit exit_devpts_fs(void) ...@@ -181,6 +193,7 @@ static void __exit exit_devpts_fs(void)
{ {
unregister_filesystem(&devpts_fs_type); unregister_filesystem(&devpts_fs_type);
mntput(devpts_mnt); mntput(devpts_mnt);
exit_devpts_xattr();
} }
module_init(init_devpts_fs) module_init(init_devpts_fs)
......
/*
File: fs/devpts/xattr.c
Derived from fs/ext3/xattr.c, changed in the following ways:
drop everything related to persistent storage of EAs
pass dentry rather than inode to internal methods
only presently define a handler for security modules
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/semaphore.h>
#include "xattr.h"
static struct devpts_xattr_handler *devpts_xattr_handlers[DEVPTS_XATTR_INDEX_MAX];
static rwlock_t devpts_handler_lock = RW_LOCK_UNLOCKED;
int
devpts_xattr_register(int name_index, struct devpts_xattr_handler *handler)
{
int error = -EINVAL;
if (name_index > 0 && name_index <= DEVPTS_XATTR_INDEX_MAX) {
write_lock(&devpts_handler_lock);
if (!devpts_xattr_handlers[name_index-1]) {
devpts_xattr_handlers[name_index-1] = handler;
error = 0;
}
write_unlock(&devpts_handler_lock);
}
return error;
}
void
devpts_xattr_unregister(int name_index, struct devpts_xattr_handler *handler)
{
if (name_index > 0 || name_index <= DEVPTS_XATTR_INDEX_MAX) {
write_lock(&devpts_handler_lock);
devpts_xattr_handlers[name_index-1] = NULL;
write_unlock(&devpts_handler_lock);
}
}
static inline const char *
strcmp_prefix(const char *a, const char *a_prefix)
{
while (*a_prefix && *a == *a_prefix) {
a++;
a_prefix++;
}
return *a_prefix ? NULL : a;
}
/*
* Decode the extended attribute name, and translate it into
* the name_index and name suffix.
*/
static inline struct devpts_xattr_handler *
devpts_xattr_resolve_name(const char **name)
{
struct devpts_xattr_handler *handler = NULL;
int i;
if (!*name)
return NULL;
read_lock(&devpts_handler_lock);
for (i=0; i<DEVPTS_XATTR_INDEX_MAX; i++) {
if (devpts_xattr_handlers[i]) {
const char *n = strcmp_prefix(*name,
devpts_xattr_handlers[i]->prefix);
if (n) {
handler = devpts_xattr_handlers[i];
*name = n;
break;
}
}
}
read_unlock(&devpts_handler_lock);
return handler;
}
static inline struct devpts_xattr_handler *
devpts_xattr_handler(int name_index)
{
struct devpts_xattr_handler *handler = NULL;
if (name_index > 0 && name_index <= DEVPTS_XATTR_INDEX_MAX) {
read_lock(&devpts_handler_lock);
handler = devpts_xattr_handlers[name_index-1];
read_unlock(&devpts_handler_lock);
}
return handler;
}
/*
* Inode operation getxattr()
*
* dentry->d_inode->i_sem down
*/
ssize_t
devpts_getxattr(struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
struct devpts_xattr_handler *handler;
handler = devpts_xattr_resolve_name(&name);
if (!handler)
return -EOPNOTSUPP;
return handler->get(dentry, name, buffer, size);
}
/*
* Inode operation listxattr()
*
* dentry->d_inode->i_sem down
*/
ssize_t
devpts_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
{
struct devpts_xattr_handler *handler = NULL;
int i, error = 0;
unsigned int size = 0;
char *buf;
read_lock(&devpts_handler_lock);
for (i=0; i<DEVPTS_XATTR_INDEX_MAX; i++) {
handler = devpts_xattr_handlers[i];
if (handler)
size += handler->list(dentry, NULL);
}
if (!buffer) {
error = size;
goto out;
} else {
error = -ERANGE;
if (size > buffer_size)
goto out;
}
buf = buffer;
for (i=0; i<DEVPTS_XATTR_INDEX_MAX; i++) {
handler = devpts_xattr_handlers[i];
if (handler)
buf += handler->list(dentry, buf);
}
error = size;
out:
read_unlock(&devpts_handler_lock);
return size;
}
/*
* Inode operation setxattr()
*
* dentry->d_inode->i_sem down
*/
int
devpts_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct devpts_xattr_handler *handler;
if (size == 0)
value = ""; /* empty EA, do not remove */
handler = devpts_xattr_resolve_name(&name);
if (!handler)
return -EOPNOTSUPP;
return handler->set(dentry, name, value, size, flags);
}
/*
* Inode operation removexattr()
*
* dentry->d_inode->i_sem down
*/
int
devpts_removexattr(struct dentry *dentry, const char *name)
{
struct devpts_xattr_handler *handler;
handler = devpts_xattr_resolve_name(&name);
if (!handler)
return -EOPNOTSUPP;
return handler->set(dentry, name, NULL, 0, XATTR_REPLACE);
}
int __init
init_devpts_xattr(void)
{
#ifdef CONFIG_DEVPTS_FS_SECURITY
int err;
err = devpts_xattr_register(DEVPTS_XATTR_INDEX_SECURITY,
&devpts_xattr_security_handler);
if (err)
return err;
#endif
return 0;
}
void
exit_devpts_xattr(void)
{
#ifdef CONFIG_DEVPTS_FS_SECURITY
devpts_xattr_unregister(DEVPTS_XATTR_INDEX_SECURITY,
&devpts_xattr_security_handler);
#endif
}
/*
File: fs/devpts/xattr.h
Derived from fs/ext3/xattr.h, changed in the following ways:
drop everything related to persistent storage of EAs
pass dentry rather than inode to internal methods
only presently define a handler for security modules
*/
#include <linux/config.h>
#include <linux/xattr.h>
/* Name indexes */
#define DEVPTS_XATTR_INDEX_MAX 10
#define DEVPTS_XATTR_INDEX_SECURITY 1
# ifdef CONFIG_DEVPTS_FS_XATTR
struct devpts_xattr_handler {
char *prefix;
size_t (*list)(struct dentry *dentry, char *buffer);
int (*get)(struct dentry *dentry, const char *name, void *buffer,
size_t size);
int (*set)(struct dentry *dentry, const char *name, const void *buffer,
size_t size, int flags);
};
extern int devpts_xattr_register(int, struct devpts_xattr_handler *);
extern void devpts_xattr_unregister(int, struct devpts_xattr_handler *);
extern int devpts_setxattr(struct dentry *, const char *, const void *, size_t, int);
extern ssize_t devpts_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t devpts_listxattr(struct dentry *, char *, size_t);
extern int devpts_removexattr(struct dentry *, const char *);
extern int init_devpts_xattr(void);
extern void exit_devpts_xattr(void);
# else /* CONFIG_DEVPTS_FS_XATTR */
# define devpts_setxattr NULL
# define devpts_getxattr NULL
# define devpts_listxattr NULL
# define devpts_removexattr NULL
static inline int
init_devpts_xattr(void)
{
return 0;
}
static inline void
exit_devpts_xattr(void)
{
}
# endif /* CONFIG_DEVPTS_FS_XATTR */
extern struct devpts_xattr_handler devpts_xattr_security_handler;
/*
* File: fs/devpts/xattr_security.c
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/security.h>
#include "xattr.h"
#define XATTR_SECURITY_PREFIX "security."
static size_t
devpts_xattr_security_list(struct dentry *dentry, char *buffer)
{
return security_inode_listsecurity(dentry, buffer);
}
static int
devpts_xattr_security_get(struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") == 0)
return -EINVAL;
return security_inode_getsecurity(dentry, name, buffer, size);
}
static int
devpts_xattr_security_set(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") == 0)
return -EINVAL;
return security_inode_setsecurity(dentry, name, value, size, flags);
}
struct devpts_xattr_handler devpts_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.list = devpts_xattr_security_list,
.get = devpts_xattr_security_get,
.set = devpts_xattr_security_set,
};
...@@ -376,6 +376,25 @@ struct swap_info_struct; ...@@ -376,6 +376,25 @@ struct swap_info_struct;
* Check permission before removing the extended attribute * Check permission before removing the extended attribute
* identified by @name for @dentry. * identified by @name for @dentry.
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @inode_getsecurity:
* Copy the extended attribute representation of the security label
* associated with @name for @dentry into @buffer. @buffer may be
* 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.
* Return number of bytes used/required on success.
* @inode_setsecurity:
* Set the security label associated with @name for @dentry from the
* extended attribute value @value. @size indicates the size of the
* @value in bytes. @flags may be XATTR_CREATE, XATTR_REPLACE, or 0.
* Note that @name is the remainder of the attribute name after the
* security. prefix has been removed.
* Return 0 on success.
* @inode_listsecurity:
* Copy the extended attribute names for the security labels
* associated with @dentry into @buffer. @buffer may be NULL to
* request the size of the buffer required.
* Returns number of bytes used/required on success.
* *
* Security hooks for file operations * Security hooks for file operations
* *
...@@ -1049,6 +1068,9 @@ struct security_operations { ...@@ -1049,6 +1068,9 @@ 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 dentry *dentry, const char *name, void *buffer, size_t size);
int (*inode_setsecurity)(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
int (*inode_listsecurity)(struct dentry *dentry, char *buffer);
int (*file_permission) (struct file * file, int mask); int (*file_permission) (struct file * file, int mask);
int (*file_alloc_security) (struct file * file); int (*file_alloc_security) (struct file * file);
...@@ -1499,6 +1521,21 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name) ...@@ -1499,6 +1521,21 @@ 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 dentry *dentry, const char *name, void *buffer, size_t size)
{
return security_ops->inode_getsecurity(dentry, name, buffer, size);
}
static inline int security_inode_setsecurity(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
{
return security_ops->inode_setsecurity(dentry, name, value, size, flags);
}
static inline int security_inode_listsecurity(struct dentry *dentry, char *buffer)
{
return security_ops->inode_listsecurity(dentry, buffer);
}
static inline int security_file_permission (struct file *file, int mask) static inline int security_file_permission (struct file *file, int mask)
{ {
return security_ops->file_permission (file, mask); return security_ops->file_permission (file, mask);
...@@ -2117,6 +2154,21 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name) ...@@ -2117,6 +2154,21 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return 0; return 0;
} }
static inline int security_inode_getsecurity(struct dentry *dentry, const char *name, void *buffer, size_t size)
{
return -EOPNOTSUPP;
}
static inline int security_inode_setsecurity(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
{
return -EOPNOTSUPP;
}
static inline int security_inode_listsecurity(struct dentry *dentry, char *buffer)
{
return 0;
}
static inline int security_file_permission (struct file *file, int mask) static inline int security_file_permission (struct file *file, int mask)
{ {
return 0; return 0;
......
...@@ -354,6 +354,21 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name) ...@@ -354,6 +354,21 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name)
return 0; return 0;
} }
static int dummy_inode_getsecurity(struct dentry *dentry, const char *name, void *buffer, size_t size)
{
return -EOPNOTSUPP;
}
static int dummy_inode_setsecurity(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
{
return -EOPNOTSUPP;
}
static int dummy_inode_listsecurity(struct dentry *dentry, char *buffer)
{
return 0;
}
static int dummy_file_permission (struct file *file, int mask) static int dummy_file_permission (struct file *file, int mask)
{ {
return 0; return 0;
...@@ -825,6 +840,9 @@ void security_fixup_ops (struct security_operations *ops) ...@@ -825,6 +840,9 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, inode_getxattr); set_to_dummy_if_null(ops, inode_getxattr);
set_to_dummy_if_null(ops, inode_listxattr); set_to_dummy_if_null(ops, inode_listxattr);
set_to_dummy_if_null(ops, inode_removexattr); set_to_dummy_if_null(ops, inode_removexattr);
set_to_dummy_if_null(ops, inode_getsecurity);
set_to_dummy_if_null(ops, inode_setsecurity);
set_to_dummy_if_null(ops, inode_listsecurity);
set_to_dummy_if_null(ops, file_permission); set_to_dummy_if_null(ops, file_permission);
set_to_dummy_if_null(ops, file_alloc_security); set_to_dummy_if_null(ops, file_alloc_security);
set_to_dummy_if_null(ops, file_free_security); set_to_dummy_if_null(ops, file_free_security);
......
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