[PATCH] Process Attribute API for Security Modules

From: Stephen Smalley (sdsat_private)
Date: Fri Apr 18 2003 - 10:58:34 PDT

  • Next message: Stephen Smalley: "[PATCH] Extended Attributes for Security Modules"

    Hi,
    
    This patch against 2.5.67 implements a process attribute API for
    security modules, as described in my April 8th RFC posting, and is a
    prerequisite for merging SELinux.  There have been no responses to the
    process attribute RFC, so I would ask that this patch be applied.  There
    have been two minor changes to the patch since the RFC was posted:
    1) The stub hook functions for [gs]etprocattr were changed to return
    -EINVAL rather than -ENOSYS (a holdover from an earlier syscall-based
    implementation), which seems consistent with the behavior of
    vfs_read/write when no file read/write operation is defined.  An
    alternative would be -EOPNOTSUPP.
    2) Spurious inline qualifiers on the dummy stub hooks were removed.
    
     fs/proc/base.c           |  250 +++++++++++++++++++++++++++++++++++++++++++++++
     include/linux/security.h |   23 ++++
     security/dummy.c         |   12 ++
     3 files changed, 285 insertions(+)
    
    Index: linux-2.5/fs/proc/base.c
    diff -u linux-2.5/fs/proc/base.c:1.1.1.2 linux-2.5/fs/proc/base.c:1.5
    --- linux-2.5/fs/proc/base.c:1.1.1.2	Tue Apr  8 09:47:57 2003
    +++ linux-2.5/fs/proc/base.c	Tue Apr  8 10:15:57 2003
    @@ -58,6 +58,10 @@
     	PROC_PID_MAPS,
     	PROC_PID_MOUNTS,
     	PROC_PID_WCHAN,
    +	PROC_PID_ATTR,
    +	PROC_PID_ATTR_CURRENT,
    +	PROC_PID_ATTR_EXEC,
    +	PROC_PID_ATTR_FSCREATE,
     	PROC_PID_FD_DIR = 0x8000,	/* 0x8000-0xffff */
     };
     
    @@ -82,11 +86,18 @@
       E(PROC_PID_ROOT,	"root",		S_IFLNK|S_IRWXUGO),
       E(PROC_PID_EXE,	"exe",		S_IFLNK|S_IRWXUGO),
       E(PROC_PID_MOUNTS,	"mounts",	S_IFREG|S_IRUGO),
    +  E(PROC_PID_ATTR,	"attr",		S_IFDIR|S_IRUGO|S_IXUGO),
     #ifdef CONFIG_KALLSYMS
       E(PROC_PID_WCHAN,	"wchan",	S_IFREG|S_IRUGO),
     #endif
       {0,0,NULL,0}
     };
    +static struct pid_entry attr_stuff[] = {
    +  E(PROC_PID_ATTR_CURRENT,	"current",	S_IFREG|S_IRUGO|S_IWUSR),
    +  E(PROC_PID_ATTR_EXEC,		"exec",		S_IFREG|S_IRUGO|S_IWUSR),
    +  E(PROC_PID_ATTR_FSCREATE,	"fscreate",	S_IFREG|S_IRUGO|S_IWUSR),
    +  {0,0,NULL,0}
    +};
     #undef E
     
     static inline struct task_struct *proc_task(struct inode *inode)
    @@ -961,6 +972,240 @@
     	.permission	= proc_permission,
     };
     
    +static int proc_attr_readdir(struct file * filp,
    +	void * dirent, filldir_t filldir)
    +{
    +	int i;
    +	int pid, ino;
    +	struct inode *inode = filp->f_dentry->d_inode;
    +	struct pid_entry *p;
    +	int ret = 0;
    +
    +	lock_kernel();
    +
    +	pid = proc_task(inode)->pid;
    +	if (!pid) {
    +		ret = -ENOENT;
    +		goto out;
    +	}
    +	i = filp->f_pos;
    +	switch (i) {
    +		case 0:
    +			if (filldir(dirent, ".", 1, i, inode->i_ino, DT_DIR) < 0)
    +				goto out;
    +			i++;
    +			filp->f_pos++;
    +			/* fall through */
    +		case 1:
    +			ino = fake_ino(pid, PROC_PID_INO);
    +			if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0)
    +				goto out;
    +			i++;
    +			filp->f_pos++;
    +			/* fall through */
    +		default:
    +			i -= 2;
    +			if (i>=sizeof(attr_stuff)/sizeof(attr_stuff[0])) {
    +				ret = 1;
    +				goto out;
    +			}
    +			p = attr_stuff + i;
    +			while (p->name) {
    +				if (filldir(dirent, p->name, p->len, filp->f_pos,
    +					    fake_ino(pid, p->type), p->mode >> 12) < 0)
    +					goto out;
    +				filp->f_pos++;
    +				p++;
    +			}
    +	}
    +
    +	ret = 1;
    +out:
    +	unlock_kernel();
    +	return ret;
    +}
    +
    +#define ATTRGET(name) \
    +static ssize_t proc_pid_attr_get##name(struct task_struct *task, char *buffer, size_t count) \
    +{ \
    +	return security_getprocattr(task, #name , buffer, count); \
    +}
    +ATTRGET(current)
    +ATTRGET(exec)
    +ATTRGET(fscreate)
    +#undef ATTRGET
    +
    +static ssize_t proc_pid_attr_read(struct file * file, char * buf,
    +				  size_t count, loff_t *ppos,
    +				  int (*attr_read)(struct task_struct *task, char *page, size_t count))
    +{
    +	struct inode * inode = file->f_dentry->d_inode;
    +	unsigned long page;
    +	ssize_t length;
    +	ssize_t end;
    +	struct task_struct *task = proc_task(inode);
    +
    +	if (count > PAGE_SIZE)
    +		count = PAGE_SIZE;
    +	if (!(page = __get_free_page(GFP_KERNEL)))
    +		return -ENOMEM;
    +
    +	length = attr_read(task, (char*)page, count);
    +	if (length < 0) {
    +		free_page(page);
    +		return length;
    +	}
    +	/* Static 4kB (or whatever) block capacity */
    +	if (*ppos >= length) {
    +		free_page(page);
    +		return 0;
    +	}
    +	if (count + *ppos > length)
    +		count = length - *ppos;
    +	end = count + *ppos;
    +	copy_to_user(buf, (char *) page + *ppos, count);
    +	*ppos = end;
    +	free_page(page);
    +	return count;
    +}
    +
    +#define ATTRREAD(name) \
    +static ssize_t proc_pid_attr_read##name(struct file * file, \
    +					char * buf, \
    +					size_t count, loff_t *ppos) \
    +{ \
    +	return proc_pid_attr_read(file, buf, count, ppos, proc_pid_attr_get##name); \
    +}
    +ATTRREAD(current);
    +ATTRREAD(exec);
    +ATTRREAD(fscreate);
    +#undef ATTRREAD
    +
    +#define ATTRSET(name) \
    +static ssize_t proc_pid_attr_set##name(struct task_struct *task, char *buffer, size_t count) \
    +{ \
    +	return security_setprocattr(task, #name , buffer, count); \
    +}
    +ATTRSET(current)
    +ATTRSET(exec)
    +ATTRSET(fscreate)
    +#undef ATTRSET
    +
    +static ssize_t proc_pid_attr_write(struct file * file, const char * buf,
    +				   size_t count, loff_t *ppos,
    +				   int (*attr_write)(struct task_struct *task, char *page, size_t count))
    +{ 
    +	struct inode * inode = file->f_dentry->d_inode;
    +	char *page; 
    +	ssize_t length; 
    +	struct task_struct *task = proc_task(inode); 
    +
    +	if (count > PAGE_SIZE) 
    +		count = PAGE_SIZE; 
    +	if (*ppos != 0) {
    +		/* No partial writes. */
    +		return -EINVAL;
    +	}
    +	page = (char*)__get_free_page(GFP_USER); 
    +	if (!page) 
    +		return -ENOMEM;
    +	length = -EFAULT; 
    +	if (copy_from_user(page, buf, count)) 
    +		goto out;
    +
    +	length = attr_write(task, page, count);
    +out:
    +	free_page((unsigned long) page);
    +	return length;
    +} 
    +
    +#define ATTRWRITE(name) \
    +static ssize_t proc_pid_attr_write##name(struct file * file, \
    +					 const char * buf, \
    +					 size_t count, loff_t *ppos) \
    +{ \
    +	return proc_pid_attr_write(file, buf, count, ppos, proc_pid_attr_set##name); \
    +}
    +ATTRWRITE(current);
    +ATTRWRITE(exec);
    +ATTRWRITE(fscreate);
    +#undef ATTRWRITE
    +
    +#define ATTROPS(name) \
    +static struct file_operations proc_pid_attr_##name##_operations = { \
    +	.read		= proc_pid_attr_read##name, \
    +	.write		= proc_pid_attr_write##name, \
    +};
    +ATTROPS(current);
    +ATTROPS(exec);
    +ATTROPS(fscreate);
    +#undef ATTROPS
    +
    +static struct dentry *proc_attr_lookup(struct inode *dir, struct dentry *dentry)
    +{
    +	struct inode *inode;
    +	int error;
    +	struct task_struct *task = proc_task(dir);
    +	struct pid_entry *p;
    +	struct proc_inode *ei;
    +
    +	error = -ENOENT;
    +	inode = NULL;
    +
    +	for (p = attr_stuff; p->name; p++) {
    +		if (p->len != dentry->d_name.len)
    +			continue;
    +		if (!memcmp(dentry->d_name.name, p->name, p->len))
    +			break;
    +	}
    +	if (!p->name)
    +		goto out;
    +
    +	error = -EINVAL;
    +	inode = proc_pid_make_inode(dir->i_sb, task, p->type);
    +	if (!inode)
    +		goto out;
    +
    +	ei = PROC_I(inode);
    +	inode->i_mode = p->mode;
    +	/*
    +	 * Yes, it does not scale. And it should not. Don't add
    +	 * new entries into /proc/<pid>/attr without very good reasons.
    +	 */
    +	switch(p->type) {
    +		case PROC_PID_ATTR_CURRENT:
    +			inode->i_fop = &proc_pid_attr_current_operations;
    +			break;
    +		case PROC_PID_ATTR_EXEC:
    +			inode->i_fop = &proc_pid_attr_exec_operations;
    +			break;
    +		case PROC_PID_ATTR_FSCREATE:
    +			inode->i_fop = &proc_pid_attr_fscreate_operations;
    +			break;
    +		default:
    +			printk("procfs: impossible type (%d)",p->type);
    +			iput(inode);
    +			return ERR_PTR(-EINVAL);
    +	}
    +	dentry->d_op = &pid_dentry_operations;
    +	d_add(dentry, inode);
    +	if (!proc_task(dentry->d_inode)->pid)
    +		d_drop(dentry);
    +	return NULL;
    +
    +out:
    +	return ERR_PTR(error);
    +}
    +
    +static struct file_operations proc_attr_operations = {
    +	.read		= generic_read_dir,
    +	.readdir	= proc_attr_readdir,
    +};
    +
    +static struct inode_operations proc_attr_inode_operations = {
    +	.lookup		= proc_attr_lookup,
    +};
    +
     /* SMP-safe */
     static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
     {
    @@ -1040,6 +1285,11 @@
     			break;
     		case PROC_PID_MOUNTS:
     			inode->i_fop = &proc_mounts_operations;
    +			break;
    +		case PROC_PID_ATTR:
    +			inode->i_nlink = 2;
    +			inode->i_op = &proc_attr_inode_operations;
    +			inode->i_fop = &proc_attr_operations;
     			break;
     #ifdef CONFIG_KALLSYMS
     		case PROC_PID_WCHAN:
    Index: linux-2.5/include/linux/security.h
    diff -u linux-2.5/include/linux/security.h:1.1.1.2 linux-2.5/include/linux/security.h:1.16
    --- linux-2.5/include/linux/security.h:1.1.1.2	Wed Mar 19 09:54:58 2003
    +++ linux-2.5/include/linux/security.h	Fri Apr 18 11:17:19 2003
    @@ -1123,6 +1128,9 @@
     
     	void (*d_instantiate) (struct dentry *dentry, struct inode *inode);
     
    + 	int (*getprocattr)(struct task_struct *p, char *name, void *value, size_t size);
    + 	int (*setprocattr)(struct task_struct *p, char *name, void *value, size_t size);
    +
     #ifdef CONFIG_SECURITY_NETWORK
     	int (*unix_stream_connect) (struct socket * sock,
     				    struct socket * other, struct sock * newsk);
    @@ -1755,6 +1769,16 @@
     	security_ops->d_instantiate (dentry, inode);
     }
     
    +static inline int security_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
    +{
    +	return security_ops->getprocattr(p, name, value, size);
    +}
    +
    +static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
    +{
    +	return security_ops->setprocattr(p, name, value, size);
    +}
    +
     static inline int security_netlink_send(struct sk_buff * skb)
     {
     	return security_ops->netlink_send(skb);
    @@ -2339,6 +2367,16 @@
     
     static inline void security_d_instantiate (struct dentry *dentry, struct inode *inode)
     { }
    +
    +static inline int security_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
    +{
    +	return -EINVAL;
    +}
    +
    +static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
    +{
    +	return -EINVAL;
    +}
     
     /*
      * The netlink capability defaults need to be used inline by default
    Index: linux-2.5/security/dummy.c
    diff -u linux-2.5/security/dummy.c:1.1.1.2 linux-2.5/security/dummy.c:1.14
    --- linux-2.5/security/dummy.c:1.1.1.2	Wed Mar 19 09:59:17 2003
    +++ linux-2.5/security/dummy.c	Fri Apr 18 11:17:20 2003
    @@ -736,6 +741,16 @@
     	return;
     }
     
    +static int dummy_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
    +{
    +	return -EINVAL;
    +}
    +
    +static int dummy_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
    +{
    +	return -EINVAL;
    +}
    +
     
     struct security_operations dummy_security_ops;
     
    @@ -860,6 +876,8 @@
     	set_to_dummy_if_null(ops, register_security);
     	set_to_dummy_if_null(ops, unregister_security);
     	set_to_dummy_if_null(ops, d_instantiate);
    + 	set_to_dummy_if_null(ops, getprocattr);
    + 	set_to_dummy_if_null(ops, setprocattr);
     #ifdef CONFIG_SECURITY_NETWORK
     	set_to_dummy_if_null(ops, unix_stream_connect);
     	set_to_dummy_if_null(ops, unix_may_send);
    
    
    
    -- 
    Stephen Smalley <sdsat_private>
    National Security Agency
    
    _______________________________________________
    linux-security-module mailing list
    linux-security-moduleat_private
    http://mail.wirex.com/mailman/listinfo/linux-security-module
    



    This archive was generated by hypermail 2b30 : Fri Apr 18 2003 - 11:00:23 PDT