From: Stephen Smalley 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. I can split this patch into two separate patches for the changes to the LSM API and the devpts xattr handler if desired, although the devpts xattr handler will initially be the only user of the new hooks. If anyone has any objections to this patch, please let me know. Thanks. 25-akpm/fs/Kconfig | 22 +++ 25-akpm/fs/devpts/Makefile | 8 + 25-akpm/fs/devpts/inode.c | 15 ++ 25-akpm/fs/devpts/xattr.c | 214 +++++++++++++++++++++++++++++++++++++ 25-akpm/fs/devpts/xattr.h | 59 ++++++++++ 25-akpm/fs/devpts/xattr_security.c | 42 +++++++ 25-akpm/include/linux/security.h | 52 ++++++++ 25-akpm/security/dummy.c | 18 +++ 8 files changed, 429 insertions(+), 1 deletion(-) diff -puN fs/devpts/inode.c~devpts-xattr-handler fs/devpts/inode.c --- 25/fs/devpts/inode.c~devpts-xattr-handler Thu May 15 15:32:03 2003 +++ 25-akpm/fs/devpts/inode.c Thu May 15 15:32:03 2003 @@ -16,6 +16,7 @@ #include #include #include +#include "xattr.h" #define DEVPTS_SUPER_MAGIC 0x1cd1 @@ -130,6 +131,13 @@ static struct dentry *get_node(int 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) { struct dentry *dentry; @@ -142,6 +150,7 @@ void devpts_pty_new(int number, dev_t de inode->i_gid = config.setgid ? config.gid : current->fsgid; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; init_special_inode(inode, S_IFCHR|config.mode, device); + inode->i_op = &devpts_file_inode_operations; dentry = get_node(number); if (!IS_ERR(dentry) && !dentry->d_inode) @@ -167,7 +176,10 @@ void devpts_pty_kill(int number) 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) { devpts_mnt = kern_mount(&devpts_fs_type); err = PTR_ERR(devpts_mnt); @@ -181,6 +193,7 @@ static void __exit exit_devpts_fs(void) { unregister_filesystem(&devpts_fs_type); mntput(devpts_mnt); + exit_devpts_xattr(); } module_init(init_devpts_fs) diff -puN fs/devpts/Makefile~devpts-xattr-handler fs/devpts/Makefile --- 25/fs/devpts/Makefile~devpts-xattr-handler Thu May 15 15:32:03 2003 +++ 25-akpm/fs/devpts/Makefile Thu May 15 15:32:03 2003 @@ -5,3 +5,11 @@ obj-$(CONFIG_DEVPTS_FS) += devpts.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 diff -puN /dev/null fs/devpts/xattr.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/fs/devpts/xattr.c Thu May 15 15:32:04 2003 @@ -0,0 +1,214 @@ +/* + 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 +#include +#include +#include +#include +#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; iprefix); + 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; ilist(dentry, NULL); + } + + if (!buffer) { + error = size; + goto out; + } else { + error = -ERANGE; + if (size > buffer_size) + goto out; + } + + buf = buffer; + for (i=0; ilist(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 + +} diff -puN /dev/null fs/devpts/xattr.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/fs/devpts/xattr.h Thu May 15 15:32:04 2003 @@ -0,0 +1,59 @@ +/* + 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 +#include + +/* 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; + diff -puN /dev/null fs/devpts/xattr_security.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/fs/devpts/xattr_security.c Thu May 15 15:32:04 2003 @@ -0,0 +1,42 @@ +/* + * File: fs/devpts/xattr_security.c + */ + +#include +#include +#include +#include +#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, +}; diff -puN fs/Kconfig~devpts-xattr-handler fs/Kconfig --- 25/fs/Kconfig~devpts-xattr-handler Thu May 15 15:32:03 2003 +++ 25-akpm/fs/Kconfig Thu May 15 15:32:03 2003 @@ -827,6 +827,28 @@ config DEVPTS_FS Note that the experimental "/dev file system support" (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 + 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 bool "Virtual memory file system support (former shm fs)" help diff -puN include/linux/security.h~devpts-xattr-handler include/linux/security.h --- 25/include/linux/security.h~devpts-xattr-handler Thu May 15 15:32:03 2003 +++ 25-akpm/include/linux/security.h Thu May 15 15:32:03 2003 @@ -376,6 +376,25 @@ struct swap_info_struct; * Check permission before removing the extended attribute * identified by @name for @dentry. * 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 * @@ -1049,6 +1068,9 @@ 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 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_alloc_security) (struct file * file); @@ -1499,6 +1521,21 @@ static inline int security_inode_removex 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) { return security_ops->file_permission (file, mask); @@ -2116,6 +2153,21 @@ static inline int security_inode_removex { 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) { diff -puN security/dummy.c~devpts-xattr-handler security/dummy.c --- 25/security/dummy.c~devpts-xattr-handler Thu May 15 15:32:03 2003 +++ 25-akpm/security/dummy.c Thu May 15 15:32:03 2003 @@ -354,6 +354,21 @@ static int dummy_inode_removexattr (stru 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) { return 0; @@ -825,6 +840,9 @@ void security_fixup_ops (struct security set_to_dummy_if_null(ops, inode_getxattr); set_to_dummy_if_null(ops, inode_listxattr); 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_alloc_security); set_to_dummy_if_null(ops, file_free_security); _