Commit ea7870c8 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Process Attribute API for Security Modules

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

This updated patch against 2.5.69 merges the readdir and lookup routines
for proc_base and proc_attr, fixes the copy_to_user call in proc_attr_read
and proc_info_read, moves the new data and code within CONFIG_SECURITY, and
uses ARRAY_SIZE, per the comments from Al Viro and Andrew Morton.  As
before, this patch implements a process attribute API for security modules
via a set of nodes in a /proc/pid/attr directory.  Credit for the idea of
implementing this API via /proc/pid/attr nodes goes to Al Viro.  Jan Harkes
provided a nice cleanup of the implementation to reduce the code bloat.
parent 6f333c22
...@@ -58,6 +58,13 @@ enum pid_directory_inos { ...@@ -58,6 +58,13 @@ enum pid_directory_inos {
PROC_PID_MAPS, PROC_PID_MAPS,
PROC_PID_MOUNTS, PROC_PID_MOUNTS,
PROC_PID_WCHAN, PROC_PID_WCHAN,
#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 */ PROC_PID_FD_DIR = 0x8000, /* 0x8000-0xffff */
}; };
...@@ -82,11 +89,23 @@ static struct pid_entry base_stuff[] = { ...@@ -82,11 +89,23 @@ static struct pid_entry base_stuff[] = {
E(PROC_PID_ROOT, "root", S_IFLNK|S_IRWXUGO), E(PROC_PID_ROOT, "root", S_IFLNK|S_IRWXUGO),
E(PROC_PID_EXE, "exe", 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_MOUNTS, "mounts", S_IFREG|S_IRUGO),
#ifdef CONFIG_SECURITY
E(PROC_PID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO),
#endif
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
E(PROC_PID_WCHAN, "wchan", S_IFREG|S_IRUGO), E(PROC_PID_WCHAN, "wchan", S_IFREG|S_IRUGO),
#endif #endif
{0,0,NULL,0} {0,0,NULL,0}
}; };
#ifdef CONFIG_SECURITY
static struct pid_entry attr_stuff[] = {
E(PROC_PID_ATTR_CURRENT, "current", S_IFREG|S_IRUGO|S_IWUSR),
E(PROC_PID_ATTR_PREV, "prev", 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}
};
#endif
#undef E #undef E
static inline struct task_struct *proc_task(struct inode *inode) static inline struct task_struct *proc_task(struct inode *inode)
...@@ -411,7 +430,9 @@ static ssize_t proc_info_read(struct file * file, char * buf, ...@@ -411,7 +430,9 @@ static ssize_t proc_info_read(struct file * file, char * buf,
if (count + *ppos > length) if (count + *ppos > length)
count = length - *ppos; count = length - *ppos;
end = count + *ppos; end = count + *ppos;
copy_to_user(buf, (char *) page + *ppos, count); if (copy_to_user(buf, (char *) page + *ppos, count))
count = -EFAULT;
else
*ppos = end; *ppos = end;
free_page(page); free_page(page);
return count; return count;
...@@ -698,13 +719,16 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) ...@@ -698,13 +719,16 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
return retval; return retval;
} }
static int proc_base_readdir(struct file * filp, static int proc_pident_readdir(struct file *filp,
void * dirent, filldir_t filldir) void *dirent, filldir_t filldir,
struct pid_entry *ents, unsigned int nents)
{ {
int i; int i;
int pid; 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; struct pid_entry *p;
ino_t ino;
int ret; int ret;
ret = -ENOENT; ret = -ENOENT;
...@@ -716,24 +740,26 @@ static int proc_base_readdir(struct file * filp, ...@@ -716,24 +740,26 @@ static int proc_base_readdir(struct file * filp,
i = filp->f_pos; i = filp->f_pos;
switch (i) { switch (i) {
case 0: 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)
goto out; goto out;
i++; i++;
filp->f_pos++; filp->f_pos++;
/* fall through */ /* fall through */
case 1: case 1:
if (filldir(dirent, "..", 2, i, PROC_ROOT_INO, DT_DIR) < 0) ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
goto out; goto out;
i++; i++;
filp->f_pos++; filp->f_pos++;
/* fall through */ /* fall through */
default: default:
i -= 2; i -= 2;
if (i >= ARRAY_SIZE(base_stuff)) { if (i >= nents) {
ret = 1; ret = 1;
goto out; goto out;
} }
p = base_stuff + i; p = ents + i;
while (p->name) { while (p->name) {
if (filldir(dirent, p->name, p->len, filp->f_pos, if (filldir(dirent, p->name, p->len, filp->f_pos,
fake_ino(pid, p->type), p->mode >> 12) < 0) fake_ino(pid, p->type), p->mode >> 12) < 0)
...@@ -748,6 +774,13 @@ static int proc_base_readdir(struct file * filp, ...@@ -748,6 +774,13 @@ static int proc_base_readdir(struct file * filp,
return ret; return ret;
} }
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 */ /* building an inode */
static int task_dumpable(struct task_struct *task) static int task_dumpable(struct task_struct *task)
...@@ -975,8 +1008,86 @@ static struct inode_operations proc_fd_inode_operations = { ...@@ -975,8 +1008,86 @@ static struct inode_operations proc_fd_inode_operations = {
.permission = proc_permission, .permission = proc_permission,
}; };
#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 = proc_task(inode);
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 = 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 = 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
/* SMP-safe */ /* SMP-safe */
static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) static struct dentry *proc_pident_lookup(struct inode *dir,
struct dentry *dentry,
struct pid_entry *ents)
{ {
struct inode *inode; struct inode *inode;
int error; int error;
...@@ -990,7 +1101,7 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) ...@@ -990,7 +1101,7 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
if (!pid_alive(task)) if (!pid_alive(task))
goto out; goto out;
for (p = base_stuff; p->name; p++) { for (p = ents; p->name; p++) {
if (p->len != dentry->d_name.len) if (p->len != dentry->d_name.len)
continue; continue;
if (!memcmp(dentry->d_name.name, p->name, p->len)) if (!memcmp(dentry->d_name.name, p->name, p->len))
...@@ -1058,6 +1169,19 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) ...@@ -1058,6 +1169,19 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
case PROC_PID_MOUNTS: case PROC_PID_MOUNTS:
inode->i_fop = &proc_mounts_operations; inode->i_fop = &proc_mounts_operations;
break; 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
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
case PROC_PID_WCHAN: case PROC_PID_WCHAN:
inode->i_fop = &proc_info_file_operations; inode->i_fop = &proc_info_file_operations;
...@@ -1077,6 +1201,10 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) ...@@ -1077,6 +1201,10 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
return ERR_PTR(error); 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 = { static struct file_operations proc_base_operations = {
.read = generic_read_dir, .read = generic_read_dir,
.readdir = proc_base_readdir, .readdir = proc_base_readdir,
...@@ -1086,6 +1214,28 @@ static struct inode_operations proc_base_inode_operations = { ...@@ -1086,6 +1214,28 @@ static struct inode_operations proc_base_inode_operations = {
.lookup = proc_base_lookup, .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: * /proc/self:
*/ */
......
...@@ -1128,6 +1128,9 @@ struct security_operations { ...@@ -1128,6 +1128,9 @@ struct security_operations {
void (*d_instantiate) (struct dentry *dentry, struct inode *inode); 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 #ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct socket * sock, int (*unix_stream_connect) (struct socket * sock,
struct socket * other, struct sock * newsk); struct socket * other, struct sock * newsk);
...@@ -1766,6 +1769,16 @@ static inline void security_d_instantiate (struct dentry *dentry, struct inode * ...@@ -1766,6 +1769,16 @@ static inline void security_d_instantiate (struct dentry *dentry, struct inode *
security_ops->d_instantiate (dentry, inode); 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) static inline int security_netlink_send(struct sk_buff * skb)
{ {
return security_ops->netlink_send(skb); return security_ops->netlink_send(skb);
...@@ -2355,6 +2368,16 @@ static inline int security_sem_semop (struct sem_array * sma, ...@@ -2355,6 +2368,16 @@ static inline int security_sem_semop (struct sem_array * sma,
static inline void security_d_instantiate (struct dentry *dentry, struct inode *inode) 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 * The netlink capability defaults need to be used inline by default
* (rather than hooking into the capability module) to reduce overhead * (rather than hooking into the capability module) to reduce overhead
......
...@@ -741,6 +741,16 @@ static void dummy_d_instantiate (struct dentry *dentry, struct inode *inode) ...@@ -741,6 +741,16 @@ static void dummy_d_instantiate (struct dentry *dentry, struct inode *inode)
return; 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; struct security_operations dummy_security_ops;
...@@ -866,6 +876,8 @@ void security_fixup_ops (struct security_operations *ops) ...@@ -866,6 +876,8 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, register_security); set_to_dummy_if_null(ops, register_security);
set_to_dummy_if_null(ops, unregister_security); set_to_dummy_if_null(ops, unregister_security);
set_to_dummy_if_null(ops, d_instantiate); 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 #ifdef CONFIG_SECURITY_NETWORK
set_to_dummy_if_null(ops, unix_stream_connect); set_to_dummy_if_null(ops, unix_stream_connect);
set_to_dummy_if_null(ops, unix_may_send); set_to_dummy_if_null(ops, unix_may_send);
......
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