[PATCH] Backport of Process Attribute API for Security Modules

From: James Carter (jwcart2@private)
Date: Fri Sep 26 2003 - 08:10:02 PDT

  • Next message: James Carter: "[PATCH] Backport of security_inode_post_setxattr hook"

    This patch against lsm-2.4 is a backport of the changes accepted into
    the 2.5/6 kernel to add a process attribute API for security modules. 
    It adds /proc/pid/attr directories with current, prev, exec, and
    fscreate nodes, and it adds [gs]etprocattr hooks functions.  The new
    2.4-based SELinux (a back port of the 2.6-based SELinux) depends on
    these, so we'd like to get them into the lsm-2.4 tree so that we can
    also merge the new 2.4-based SELinux into it.  
    
    This is the first of a series of patches backporting the changes to the
    LSM API required for the new SELinux (all of which are already in 2.6).
    
    For reference see Steve's email to the LSM list on 8 Apr 2003:
    http://mail.wirex.com/pipermail/linux-security-module/2003-April/4265.html 
    If there are no objections, I will ask Steve to merge it.
    
    
     fs/proc/base.c           |  172 +++++++++++++++++++++++++++++++++++++++++++----
     include/linux/security.h |   22 ++++++
     security/dummy.c         |   12 +++
     3 files changed, 195 insertions(+), 11 deletions(-)
    
    diff -Nru a/fs/proc/base.c b/fs/proc/base.c
    --- a/fs/proc/base.c	Thu Sep 25 15:15:39 2003
    +++ b/fs/proc/base.c	Thu Sep 25 15:15:39 2003
    @@ -359,8 +359,10 @@
     	if (count + *ppos > length)
     		count = length - *ppos;
     	end = count + *ppos;
    -	copy_to_user(buf, (char *) page + *ppos, count);
    -	*ppos = end;
    +	if (copy_to_user(buf, (char *) page + *ppos, count))
    +		count = -EFAULT;
    +	else
    +		*ppos = end;
     	free_page(page);
     	return count;
     }
    @@ -583,6 +585,13 @@
     	PROC_PID_MAPS,
     	PROC_PID_CPU,
     	PROC_PID_MOUNTS,
    +#ifdef CONFIG_SECURITY
    +	PROC_PID_ATTR,
    +	PROC_PID_ATTR_CURRENT,
    +	PROC_PID_ATTR_PREV,
    +	PROC_PID_ATTR_EXEC,
    +	PROC_PID_ATTR_FSCREATE,
    +#endif
     	PROC_PID_FD_DIR = 0x8000,	/* 0x8000-0xffff */
     };
     
    @@ -603,8 +612,20 @@
       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),
    +#ifdef CONFIG_SECURITY
    +  E(PROC_PID_ATTR,	"attr",		S_IFDIR|S_IRUGO|S_IXUGO),
    +#endif
    +  {0,0,NULL,0}
    +};
    +#ifdef CONFIG_SECURITY
    +static struct pid_entry attr_stuff[] = {
    +  E(PROC_PID_ATTR_CURRENT,	"current",	S_IFREG|S_IRUGO|S_IWUGO),
    +  E(PROC_PID_ATTR_PREV,	"prev",	S_IFREG|S_IRUGO),
    +  E(PROC_PID_ATTR_EXEC,	"exec",	S_IFREG|S_IRUGO|S_IWUGO),
    +  E(PROC_PID_ATTR_FSCREATE,	"fscreate",	S_IFREG|S_IRUGO|S_IWUGO),
       {0,0,NULL,0}
     };
    +#endif
     #undef E
     
     #define NUMBUF 10
    @@ -672,13 +693,16 @@
     	return retval;
     }
     
    -static int proc_base_readdir(struct file * filp,
    -	void * dirent, filldir_t filldir)
    +static int proc_pident_readdir(struct file *filp,
    +		void *dirent, filldir_t filldir,
    +		struct pid_entry *ents, unsigned int nents)
     {
     	int i;
     	int pid;
    -	struct inode *inode = filp->f_dentry->d_inode;
    +	struct dentry *dentry = filp->f_dentry;
    +	struct inode *inode = dentry->d_inode;
     	struct pid_entry *p;
    +	ino_t ino;
     
     	pid = inode->u.proc_i.task->pid;
     	if (!pid)
    @@ -686,22 +710,24 @@
     	i = filp->f_pos;
     	switch (i) {
     		case 0:
    -			if (filldir(dirent, ".", 1, i, inode->i_ino, DT_DIR) < 0)
    +			ino = inode->i_ino;
    +			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
     				return 0;
     			i++;
     			filp->f_pos++;
     			/* fall through */
     		case 1:
    -			if (filldir(dirent, "..", 2, i, PROC_ROOT_INO, DT_DIR) < 0)
    +			ino = dentry->d_parent->d_inode->i_ino;
    +			if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
     				return 0;
     			i++;
     			filp->f_pos++;
     			/* fall through */
     		default:
     			i -= 2;
    -			if (i>=sizeof(base_stuff)/sizeof(base_stuff[0]))
    +			if (i >= nents)
     				return 1;
    -			p = base_stuff + i;
    +			p = ents + i;
     			while (p->name) {
     				if (filldir(dirent, p->name, p->len, filp->f_pos,
     					    fake_ino(pid, p->type), p->mode >> 12) < 0)
    @@ -713,6 +739,13 @@
     	return 1;
     }
     
    +static int proc_base_readdir(struct file * filp,
    +			     void * dirent, filldir_t filldir)
    +{
    +	return proc_pident_readdir(filp,dirent,filldir,
    +				   base_stuff,ARRAY_SIZE(base_stuff));
    +}
    +
     /* building an inode */
     
     static int task_dumpable(struct task_struct *task)
    @@ -888,7 +921,85 @@
     	permission:	proc_permission,
     };
     
    -static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
    +#ifdef CONFIG_SECURITY
    +static ssize_t proc_pid_attr_read(struct file * file, char * buf,
    +				  size_t count, loff_t *ppos)
    +{
    +	struct inode * inode = file->f_dentry->d_inode;
    +	unsigned long page;
    +	ssize_t length;
    +	ssize_t end;
    +	struct task_struct *task = inode->u.proc_i.task;
    +
    +	if (count > PAGE_SIZE)
    +		count = PAGE_SIZE;
    +	if (!(page = __get_free_page(GFP_KERNEL)))
    +		return -ENOMEM;
    +
    +	length = security_getprocattr(task, 
    +				      (char*)file->f_dentry->d_name.name, 
    +				      (void*)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;
    +	if (copy_to_user(buf, (char *) page + *ppos, count))
    +		count = -EFAULT;
    +	else
    +		*ppos = end;
    +	free_page(page);
    +	return count;
    +}
    +
    +static ssize_t proc_pid_attr_write(struct file * file, const char * buf,
    +				   size_t count, loff_t *ppos)
    +{ 
    +	struct inode * inode = file->f_dentry->d_inode;
    +	char *page; 
    +	ssize_t length; 
    +	struct task_struct *task = inode->u.proc_i.task; 
    +
    +	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 = security_setprocattr(task, 
    +				      (char*)file->f_dentry->d_name.name, 
    +				      (void*)page, count);
    +out:
    +	free_page((unsigned long) page);
    +	return length;
    +} 
    +
    +static struct file_operations proc_pid_attr_operations = {
    +	.read		= proc_pid_attr_read,
    +	.write		= proc_pid_attr_write,
    +};
    +
    +static struct file_operations proc_attr_operations;
    +static struct inode_operations proc_attr_inode_operations;
    +#endif
    +
    +static struct dentry *proc_pident_lookup(struct inode *dir, 
    +					 struct dentry *dentry,
    +					 struct pid_entry *ents)
     {
     	struct inode *inode;
     	int error;
    @@ -898,7 +1009,7 @@
     	error = -ENOENT;
     	inode = NULL;
     
    -	for (p = base_stuff; p->name; p++) {
    +	for (p = ents; p->name; p++) {
     		if (p->len != dentry->d_name.len)
     			continue;
     		if (!memcmp(dentry->d_name.name, p->name, p->len))
    @@ -971,6 +1082,19 @@
     		case PROC_PID_MOUNTS:
     			inode->i_fop = &proc_mounts_operations;
     			break;
    +#ifdef CONFIG_SECURITY
    +		case PROC_PID_ATTR:
    +			inode->i_nlink = 2;
    +			inode->i_op = &proc_attr_inode_operations;
    +			inode->i_fop = &proc_attr_operations;
    +			break;
    +		case PROC_PID_ATTR_CURRENT:
    +		case PROC_PID_ATTR_PREV:
    +		case PROC_PID_ATTR_EXEC:
    +		case PROC_PID_ATTR_FSCREATE:
    +			inode->i_fop = &proc_pid_attr_operations;
    +			break;
    +#endif
     		default:
     			printk("procfs: impossible type (%d)",p->type);
     			iput(inode);
    @@ -984,6 +1108,10 @@
     	return ERR_PTR(error);
     }
     
    +static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry){
    +	return proc_pident_lookup(dir, dentry, base_stuff);
    +}
    +
     static struct file_operations proc_base_operations = {
     	read:		generic_read_dir,
     	readdir:	proc_base_readdir,
    @@ -992,6 +1120,28 @@
     static struct inode_operations proc_base_inode_operations = {
     	lookup:		proc_base_lookup,
     };
    +
    +#ifdef CONFIG_SECURITY
    +static int proc_attr_readdir(struct file * filp,
    +			     void * dirent, filldir_t filldir)
    +{
    +	return proc_pident_readdir(filp,dirent,filldir,
    +				   attr_stuff,ARRAY_SIZE(attr_stuff));
    +}
    +
    +static struct file_operations proc_attr_operations = {
    +	.read		= generic_read_dir,
    +	.readdir	= proc_attr_readdir,
    +};
    +
    +static struct dentry *proc_attr_lookup(struct inode *dir, struct dentry *dentry){
    +	return proc_pident_lookup(dir, dentry, attr_stuff);
    +}
    +
    +static struct inode_operations proc_attr_inode_operations = {
    +	.lookup		= proc_attr_lookup,
    +};
    +#endif
     
     /*
      * /proc/self:
    diff -Nru a/include/linux/security.h b/include/linux/security.h
    --- a/include/linux/security.h	Thu Sep 25 15:15:39 2003
    +++ b/include/linux/security.h	Thu Sep 25 15:15:39 2003
    @@ -1394,6 +1394,8 @@
     	                            struct security_operations *ops);
     
     	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,
    @@ -2153,6 +2155,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);
    +}
    +
     /* prototypes */
     extern int security_scaffolding_startup	(void);
     extern int register_security	(struct security_operations *ops);
    @@ -2822,6 +2834,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;
    +}
     
     #endif	/* CONFIG_SECURITY */
     
    diff -Nru a/security/dummy.c b/security/dummy.c
    --- a/security/dummy.c	Thu Sep 25 15:15:39 2003
    +++ b/security/dummy.c	Thu Sep 25 15:15:39 2003
    @@ -930,6 +930,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;
     
    @@ -1059,6 +1069,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);
     	set_to_dummy_if_null(ops, sethostname);
     	set_to_dummy_if_null(ops, setdomainname);
     	set_to_dummy_if_null(ops, reboot);
    
    -- 
    James Carter <jwcart2@private>
    National Security Agency
    



    This archive was generated by hypermail 2b30 : Fri Sep 26 2003 - 08:10:39 PDT