linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Introduce credential record
@ 2007-09-19 16:17 David Howells
  2007-09-19 16:17 ` [PATCH 1/3] CRED: Introduce a COW credentials record David Howells
                   ` (2 more replies)
  0 siblings, 3 replies; 22+ messages in thread
From: David Howells @ 2007-09-19 16:17 UTC (permalink / raw)
  To: viro, hch, Trond.Myklebust, sds, casey
  Cc: linux-kernel, selinux, linux-security-module, dhowells



Hi Al, Christoph, Trond, Stephen, Casey,

Here's a set of patches that implement a very basic set of COW credentials.  It
compiles, links and runs for x86_64 with EXT3, (V)FAT, NFS, AFS, SELinux and
keyrings all enabled.  Most other filesystems are disabled, apart from things
like proc.  It is not intended to completely cover the kernel at this point.

The cred struct contains the credentials that the kernel needs to act upon
something or to create something.  Credentials that govern how a task may be
acted upon remain in the task struct.

Because keyrings and effective capabilities can be installed or changed in one
process by another process, they are shadowed by the cred structure rather than
residing there.  Additionally, the session and process keyrings are shared
between all the threads of a process.  The shadowing is performed by
update_current_cred() which is invoked on entry to any system call that might
need it.

A thread's cred struct may be read by that thread without any RCU precautions
as only that thread may replace the its own cred struct.  To change a thread's
credentials, dup_cred() should be called to create a new copy, the copy should
be changed, and then set_current_cred() should be called to make it live.  Once
live, it may not be changed as it may then be shared with file descriptors, RPC
calls and other threads.  RCU will be used to dispose of the old structure.


The three patches are:

 (1) Introduce struct cred and migrate fsuid, fsgid, the groups list and the
     keyrings pointer to it.

 (2) Introduce a security pointer into the cred struct and add LSM hooks to
     duplicate the information pointed to thereby and to free it.

     Make SELinux implement the hooks, splitting out some the task security
     data to be associated with struct cred instead.

 (3) Migrate the effective capabilities mask into the cred struct.


I plan on adding a fourth patch that will allow the LSM security contents of a
cred struct to be manipulated by the kernel - something that cachefiles and
possibly NFSd will require.

To substitute a temporary set of credentials, the cred struct attached to the
task should be altered, like so:

	int get_privileged_creds(...)
	{
		/* get special privileged creds */
		my_special_cred = dup_cred(current->cred);
		change_fsuid(my_special_cred, 123);
	}

	int do_stuff(...)
	{
		struct cred *cred;

		/* rotate in the new creds, saving the old */
		cred = __set_current_cred(get_cred(my_special_cred));

		do_privileged_stuff();

		/* restore the old creds */
		set_current_cred(cred);
	}

One thing I'm not certain about is how this should interact with /proc, which
can display some of the stuff in the cred struct.  I think it may be necessary
to have a real cred pointer and an effective cred pointer, with the contents of
/proc coming from the real, but the effective governing what actually goes on.

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/3] CRED: Introduce a COW credentials record
  2007-09-19 16:17 [PATCH 0/3] Introduce credential record David Howells
@ 2007-09-19 16:17 ` David Howells
  2007-09-19 16:18 ` [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred David Howells
  2007-09-19 16:18 ` [PATCH 3/3] CRED: Move the effective capabilities into the cred struct David Howells
  2 siblings, 0 replies; 22+ messages in thread
From: David Howells @ 2007-09-19 16:17 UTC (permalink / raw)
  To: viro, hch, Trond.Myklebust, sds, casey
  Cc: linux-kernel, selinux, linux-security-module, dhowells

Introduce a copy on write credentials record (struct cred).  The fsuid, fsgid,
supplementary groups list move into it (DAC security).  The session, process
and thread keyrings are reflected in it, but don't primarily reside there as
they aren't per-thread and occasionally need to be instantiated or replaced by
other threads or processes.

The LSM security information (MAC security) does *not* migrate from task_struct
at this point, but will be addressed by a later patch.

task_struct then gains an RCU-governed pointer to the credentials as a
replacement to the members it lost.

struct file gains a pointer to (f_cred) and a reference on the cred struct that
the opener was using at the time the file was opened.  This replaces f_uid and
f_gid.

To alter the credentials record, a copy must be made.  This copy may then be
altered and then the pointer in the task_struct redirected to it.  From that
point on the new record should be considered immutable.

In addition, the default setting of i_uid and i_gid to fsuid and fsgid has been
moved from the callers of new_inode() into new_inode() itself.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 arch/x86_64/kernel/sys_x86_64.c  |    4 +
 fs/aio.c                         |   25 +++++-
 fs/anon_inodes.c                 |    2 
 fs/attr.c                        |    4 -
 fs/compat.c                      |   65 ++++++++++++++
 fs/compat_ioctl.c                |    7 +-
 fs/dcookies.c                    |   11 ++
 fs/devpts/inode.c                |    6 +
 fs/dquot.c                       |    2 
 fs/eventfd.c                     |    4 +
 fs/eventpoll.c                   |   16 ++++
 fs/exec.c                        |   37 +++++++-
 fs/ext3/balloc.c                 |    2 
 fs/ext3/ialloc.c                 |    4 -
 fs/fcntl.c                       |   11 ++
 fs/file_table.c                  |    3 -
 fs/filesystems.c                 |    7 +-
 fs/inode.c                       |    6 +
 fs/inotify_user.c                |   12 +++
 fs/ioctl.c                       |    7 +-
 fs/locks.c                       |    6 +
 fs/namei.c                       |   48 ++++++++---
 fs/namespace.c                   |   12 +++
 fs/nfs/inode.c                   |    2 
 fs/nfsctl.c                      |    4 +
 fs/open.c                        |  103 +++++++++++++++++++++--
 fs/pipe.c                        |    2 
 fs/posix_acl.c                   |    4 -
 fs/proc/array.c                  |   12 +--
 fs/quota.c                       |    4 +
 fs/ramfs/inode.c                 |    2 
 fs/read_write.c                  |   54 +++++++++++-
 fs/readdir.c                     |    8 ++
 fs/select.c                      |    4 +
 fs/signalfd.c                    |    4 +
 fs/splice.c                      |   12 +++
 fs/stat.c                        |   19 ++++
 fs/super.c                       |    4 +
 fs/sync.c                        |   11 ++
 fs/timerfd.c                     |    4 +
 fs/utimes.c                      |    4 +
 fs/xattr.c                       |   60 ++++++++++++-
 include/linux/binfmts.h          |    1 
 include/linux/cred.h             |  172 ++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h               |    5 +
 include/linux/init_task.h        |    4 -
 include/linux/sched.h            |    7 +-
 include/linux/sunrpc/auth.h      |   17 +---
 kernel/Makefile                  |    2 
 kernel/auditsc.c                 |   13 ++-
 kernel/cred.c                    |  161 ++++++++++++++++++++++++++++++++++++
 kernel/exit.c                    |    1 
 kernel/fork.c                    |   63 +++++++++++++-
 kernel/sys.c                     |  144 +++++++++++++++++++++++---------
 kernel/uid16.c                   |    7 +-
 mm/fadvise.c                     |    4 +
 mm/filemap.c                     |    4 +
 mm/fremap.c                      |    7 +-
 mm/madvise.c                     |    7 +-
 mm/msync.c                       |    7 +-
 mm/shmem.c                       |    6 -
 net/socket.c                     |    2 
 net/sunrpc/auth.c                |   25 +-----
 net/sunrpc/auth_gss/auth_gss.c   |    6 +
 net/sunrpc/auth_null.c           |    4 -
 net/sunrpc/auth_unix.c           |    6 +
 security/dummy.c                 |   13 ++-
 security/keys/compat.c           |    6 +
 security/keys/key.c              |    3 -
 security/keys/keyctl.c           |   16 +++-
 security/keys/permission.c       |   16 ++--
 security/keys/process_keys.c     |   58 ++++---------
 security/keys/request_key.c      |   14 ++-
 security/keys/request_key_auth.c |    2 
 74 files changed, 1172 insertions(+), 249 deletions(-)

diff --git a/arch/x86_64/kernel/sys_x86_64.c b/arch/x86_64/kernel/sys_x86_64.c
index 4770b7a..6cb691e 100644
--- a/arch/x86_64/kernel/sys_x86_64.c
+++ b/arch/x86_64/kernel/sys_x86_64.c
@@ -43,6 +43,10 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, unsigned long pr
 	long error;
 	struct file * file;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EINVAL;
 	if (off & ~PAGE_MASK)
 		goto out;
diff --git a/fs/aio.c b/fs/aio.c
index dbe699e..4bfabc1 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1249,6 +1249,10 @@ asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp)
 	unsigned long ctx;
 	long ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		goto out;
+
 	ret = get_user(ctx, ctxp);
 	if (unlikely(ret))
 		goto out;
@@ -1284,6 +1288,12 @@ out:
 asmlinkage long sys_io_destroy(aio_context_t ctx)
 {
 	struct kioctx *ioctx = lookup_ioctx(ctx);
+	int ret;
+
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;;
+
 	if (likely(NULL != ioctx)) {
 		io_destroy(ioctx);
 		return 0;
@@ -1634,6 +1644,10 @@ asmlinkage long sys_io_submit(aio_context_t ctx_id, long nr,
 	long ret = 0;
 	int i;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;;
+
 	if (unlikely(nr < 0))
 		return -EINVAL;
 
@@ -1711,6 +1725,10 @@ asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb,
 	u32 key;
 	int ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;;
+
 	ret = get_user(key, &iocb->aio_key);
 	if (unlikely(ret))
 		return -EFAULT;
@@ -1771,8 +1789,13 @@ asmlinkage long sys_io_getevents(aio_context_t ctx_id,
 				 struct timespec __user *timeout)
 {
 	struct kioctx *ioctx = lookup_ioctx(ctx_id);
-	long ret = -EINVAL;
+	long ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EINVAL;
 	if (likely(ioctx)) {
 		if (likely(min_nr <= nr && min_nr >= 0 && nr >= 0))
 			ret = read_events(ioctx, min_nr, nr, events, timeout);
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index b4a7588..921087b 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -163,8 +163,6 @@ static struct inode *anon_inode_mkinode(void)
 	 */
 	inode->i_state = I_DIRTY;
 	inode->i_mode = S_IRUSR | S_IWUSR;
-	inode->i_uid = current->fsuid;
-	inode->i_gid = current->fsgid;
 	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	return inode;
 }
diff --git a/fs/attr.c b/fs/attr.c
index f8dfc22..3e6b911 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -29,13 +29,13 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
 
 	/* Make sure a caller can chown. */
 	if ((ia_valid & ATTR_UID) &&
-	    (current->fsuid != inode->i_uid ||
+	    (current->cred->uid != inode->i_uid ||
 	     attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
 		goto error;
 
 	/* Make sure caller can chgrp. */
 	if ((ia_valid & ATTR_GID) &&
-	    (current->fsuid != inode->i_uid ||
+	    (current->cred->uid != inode->i_uid ||
 	    (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
 	    !capable(CAP_CHOWN))
 		goto error;
diff --git a/fs/compat.c b/fs/compat.c
index 15078ce..0d21573 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -238,6 +238,10 @@ asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct kstatfs tmp;
@@ -255,6 +259,10 @@ asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user
 	struct kstatfs tmp;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
@@ -303,6 +311,10 @@ asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, s
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	if (sz != sizeof(*buf))
 		return -EINVAL;
 
@@ -326,6 +338,10 @@ asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct c
 	if (sz != sizeof(*buf))
 		return -EINVAL;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
@@ -397,6 +413,10 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd,
 	struct flock f;
 	long ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	switch (cmd) {
 	case F_GETLK:
 	case F_SETLK:
@@ -723,6 +743,10 @@ asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name,
 	char *dir_page;
 	int retval;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	retval = copy_mount_options (type, &type_page);
 	if (retval < 0)
 		goto out;
@@ -821,6 +845,10 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd,
 	struct file *file;
 	struct compat_readdir_callback buf;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
@@ -900,6 +928,10 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
 	struct compat_getdents_callback buf;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EFAULT;
 	if (!access_ok(VERIFY_WRITE, dirent, count))
 		goto out;
@@ -991,6 +1023,10 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
 	struct compat_getdents_callback64 buf;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EFAULT;
 	if (!access_ok(VERIFY_WRITE, dirent, count))
 		goto out;
@@ -1140,8 +1176,13 @@ asmlinkage ssize_t
 compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget(fd);
 	if (!file)
 		return -EBADF;
@@ -1164,8 +1205,13 @@ asmlinkage ssize_t
 compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget(fd);
 	if (!file)
 		return -EBADF;
@@ -1357,6 +1403,10 @@ int compat_do_execve(char * filename,
 	struct file *file;
 	int retval;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	retval = -ENOMEM;
 	bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
 	if (!bprm)
@@ -1523,10 +1573,15 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
 {
 	fd_set_bits fds;
 	void *bits;
-	int size, max_fds, ret = -EINVAL;
+	int size, max_fds, ret;
 	struct fdtable *fdt;
 	long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EINVAL;
 	if (n < 0)
 		goto out_nofds;
 
@@ -2019,6 +2074,10 @@ asmlinkage long compat_sys_nfsservctl(int cmd,
 	mm_segment_t oldfs;
 	int err;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
 	karg = kmalloc(sizeof(*karg), GFP_USER);
 	kres = kmalloc(sizeof(*kres), GFP_USER);
 	if(!karg || !kres) {
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 5a5b711..3708b79 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -3567,10 +3567,15 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
 				unsigned long arg)
 {
 	struct file *filp;
-	int error = -EBADF;
+	int error;
 	struct ioctl_trans *t;
 	int fput_needed;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
+	error = -EBADF;
 	filp = fget_light(fd, &fput_needed);
 	if (!filp)
 		goto out;
diff --git a/fs/dcookies.c b/fs/dcookies.c
index 792cbf5..5f1d5ac 100644
--- a/fs/dcookies.c
+++ b/fs/dcookies.c
@@ -146,12 +146,16 @@ out:
 asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
 {
 	unsigned long cookie = (unsigned long)cookie64;
-	int err = -EINVAL;
+	int err;
 	char * kbuf;
 	char * path;
 	size_t pathlen;
 	struct dcookie_struct * dcs;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
 	/* we could leak path information to users
 	 * without dir read permission without this
 	 */
@@ -160,10 +164,9 @@ asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
 
 	mutex_lock(&dcookie_mutex);
 
-	if (!is_live()) {
-		err = -EINVAL;
+	err = -EINVAL;
+	if (!is_live())
 		goto out;
-	}
 
 	if (!(dcs = find_dcookie(cookie)))
 		goto out;
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 06ef9a2..af6bcff 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -172,8 +172,10 @@ int devpts_pty_new(struct tty_struct *tty)
 		return -ENOMEM;
 
 	inode->i_ino = number+2;
-	inode->i_uid = config.setuid ? config.uid : current->fsuid;
-	inode->i_gid = config.setgid ? config.gid : current->fsgid;
+	if (config.setuid)
+		inode->i_uid = config.uid;
+	if (config.setgid)
+		inode->i_gid = config.gid;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 	init_special_inode(inode, S_IFCHR|config.mode, device);
 	inode->i_private = tty;
diff --git a/fs/dquot.c b/fs/dquot.c
index de9a29f..f1748c6 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -832,7 +832,7 @@ static inline int need_print_warning(struct dquot *dquot)
 
 	switch (dquot->dq_type) {
 		case USRQUOTA:
-			return current->fsuid == dquot->dq_id;
+			return current->cred->uid == dquot->dq_id;
 		case GRPQUOTA:
 			return in_group_p(dquot->dq_id);
 	}
diff --git a/fs/eventfd.c b/fs/eventfd.c
index 2ce19c0..54621da 100644
--- a/fs/eventfd.c
+++ b/fs/eventfd.c
@@ -204,6 +204,10 @@ asmlinkage long sys_eventfd(unsigned int count)
 	struct file *file;
 	struct inode *inode;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
 	if (!ctx)
 		return -ENOMEM;
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 77b9953..1ec21ba 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1081,6 +1081,10 @@ asmlinkage long sys_epoll_create(int size)
 	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n",
 		     current, size));
 
+	error = update_current_cred();
+	if (error < 0)
+		goto error_return;
+
 	/*
 	 * Sanity check on the size parameter, and create the internal data
 	 * structure ( "struct eventpoll" ).
@@ -1128,6 +1132,10 @@ asmlinkage long sys_epoll_ctl(int epfd, int op, int fd,
 	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_ctl(%d, %d, %d, %p)\n",
 		     current, epfd, op, fd, event));
 
+	error = update_current_cred();
+	if (error < 0)
+		goto error_return;
+
 	error = -EFAULT;
 	if (ep_op_has_event(op) &&
 	    copy_from_user(&epds, event, sizeof(struct epoll_event)))
@@ -1224,6 +1232,10 @@ asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events,
 	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_wait(%d, %p, %d, %d)\n",
 		     current, epfd, events, maxevents, timeout));
 
+	error = update_current_cred();
+	if (error < 0)
+		goto error_return;
+
 	/* The maximum number of event must be greater than zero */
 	if (maxevents <= 0 || maxevents > EP_MAX_EVENTS)
 		return -EINVAL;
@@ -1279,6 +1291,10 @@ asmlinkage long sys_epoll_pwait(int epfd, struct epoll_event __user *events,
 	int error;
 	sigset_t ksigmask, sigsaved;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
diff --git a/fs/exec.c b/fs/exec.c
index c21a8cc..539ed8c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -131,6 +131,10 @@ asmlinkage long sys_uselib(const char __user * library)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
 	if (error)
 		goto out;
@@ -1140,6 +1144,11 @@ int prepare_binprm(struct linux_binprm *bprm)
 		}
 	}
 
+	/* prepare the new credentials */
+	bprm->cred = dup_cred(current->cred);
+	if (!bprm->cred)
+		return -ENOMEM;
+
 	/* fill in binprm security blob */
 	retval = security_bprm_set(bprm);
 	if (retval)
@@ -1181,7 +1190,9 @@ void compute_creds(struct linux_binprm *bprm)
 	task_lock(current);
 	unsafe = unsafe_exec(current);
 	security_bprm_apply_creds(bprm, unsafe);
+	set_current_cred(bprm->cred);
 	task_unlock(current);
+	bprm->cred = NULL;
 	security_bprm_post_apply_creds(bprm);
 }
 EXPORT_SYMBOL(compute_creds);
@@ -1347,6 +1358,10 @@ int do_execve(char * filename,
 	unsigned long env_p;
 	int retval;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	retval = -ENOMEM;
 	bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
 	if (!bprm)
@@ -1412,6 +1427,8 @@ out:
 	free_arg_pages(bprm);
 	if (bprm->security)
 		security_bprm_free(bprm);
+	if (bprm->cred)
+		put_cred(bprm->cred);
 
 out_mm:
 	if (bprm->mm)
@@ -1719,8 +1736,8 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
 	struct linux_binfmt * binfmt;
 	struct inode * inode;
 	struct file * file;
+	struct cred *cred;
 	int retval = 0;
-	int fsuid = current->fsuid;
 	int flag = 0;
 	int ispipe = 0;
 
@@ -1735,6 +1752,10 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
 		goto fail;
 	}
 
+	cred = dup_cred(current->cred);
+	if (!cred)
+		goto fail;
+
 	/*
 	 *	We cannot trust fsuid as being the "true" uid of the
 	 *	process nor do we know its entire history. We only know it
@@ -1742,13 +1763,13 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
 	 */
 	if (get_dumpable(mm) == 2) {	/* Setuid core dump mode */
 		flag = O_EXCL;		/* Stop rewrite attacks */
-		current->fsuid = 0;	/* Dump root private */
+		change_fsuid(cred, 0);	/* Dump root private */
 	}
 	set_dumpable(mm, 0);
 
 	retval = coredump_wait(exit_code);
 	if (retval < 0)
-		goto fail;
+		goto fail_cred;
 
 	/*
 	 * Clear any false indication of pending signals that might
@@ -1766,19 +1787,20 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
  	lock_kernel();
 	ispipe = format_corename(corename, core_pattern, signr);
 	unlock_kernel();
+	cred = __set_current_cred(cred);
  	if (ispipe) {
 		/* SIGPIPE can happen, but it's just never processed */
  		if(call_usermodehelper_pipe(corename+1, NULL, NULL, &file)) {
  			printk(KERN_INFO "Core dump to %s pipe failed\n",
 			       corename);
- 			goto fail_unlock;
+			goto fail_restore_cred;
  		}
  	} else
  		file = filp_open(corename,
 				 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
 				 0600);
 	if (IS_ERR(file))
-		goto fail_unlock;
+		goto fail_restore_cred;
 	inode = file->f_path.dentry->d_inode;
 	if (inode->i_nlink > 1)
 		goto close_fail;	/* multiple links - don't dump */
@@ -1802,9 +1824,12 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
 		current->signal->group_exit_code |= 0x80;
 close_fail:
 	filp_close(file, NULL);
+fail_restore_cred:
+	set_current_cred(cred);
 fail_unlock:
-	current->fsuid = fsuid;
 	complete_all(&mm->core_done);
+fail_cred:
+	put_cred(cred);
 fail:
 	return retval;
 }
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index ca8aee6..6c4e82f 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -1360,7 +1360,7 @@ static int ext3_has_free_blocks(struct ext3_sb_info *sbi)
 	free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
 	root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
 	if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
-		sbi->s_resuid != current->fsuid &&
+		sbi->s_resuid != current->cred->uid &&
 		(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
 		return 0;
 	}
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c
index e45dbd6..c10ec0c 100644
--- a/fs/ext3/ialloc.c
+++ b/fs/ext3/ialloc.c
@@ -546,15 +546,13 @@ got:
 		percpu_counter_inc(&sbi->s_dirs_counter);
 	sb->s_dirt = 1;
 
-	inode->i_uid = current->fsuid;
 	if (test_opt (sb, GRPID))
 		inode->i_gid = dir->i_gid;
 	else if (dir->i_mode & S_ISGID) {
 		inode->i_gid = dir->i_gid;
 		if (S_ISDIR(mode))
 			mode |= S_ISGID;
-	} else
-		inode->i_gid = current->fsgid;
+	}
 	inode->i_mode = mode;
 
 	inode->i_ino = ino;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 78b2ff0..042a52e 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -385,8 +385,13 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
 asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {	
 	struct file *filp;
-	long err = -EBADF;
+	long err;
+
+	err = update_current_cred();
+	if (err < 0)
+		return err;
 
+	err = -EBADF;
 	filp = fget(fd);
 	if (!filp)
 		goto out;
@@ -410,6 +415,10 @@ asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg
 	struct file * filp;
 	long err;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
 	err = -EBADF;
 	filp = fget(fd);
 	if (!filp)
diff --git a/fs/file_table.c b/fs/file_table.c
index d17fd69..f4c772c 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -115,8 +115,7 @@ struct file *get_empty_filp(void)
 	INIT_LIST_HEAD(&f->f_u.fu_list);
 	atomic_set(&f->f_count, 1);
 	rwlock_init(&f->f_owner.lock);
-	f->f_uid = tsk->fsuid;
-	f->f_gid = tsk->fsgid;
+	f->f_cred = get_current_cred();
 	eventpoll_init_file(f);
 	/* f->f_version: 0 */
 	return f;
diff --git a/fs/filesystems.c b/fs/filesystems.c
index f37f872..93fa1be 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -179,8 +179,13 @@ static int fs_maxindex(void)
  */
 asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)
 {
-	int retval = -EINVAL;
+	int retval;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
+	retval = -EINVAL;
 	switch (option) {
 		case 1:
 			retval = fs_index((const char __user *) arg1);
diff --git a/fs/inode.c b/fs/inode.c
index 29f5068..d69815e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -531,6 +531,10 @@ repeat:
  *	mapping_set_gfp_mask() must be called with suitable flags on the
  *	newly created inode's mapping
  *
+ *	The inode's user and group IDs are set to the current task's fsuid and
+ *	fsgid respectively as a default, but this should be overridden by the
+ *	caller as appropriate.
+ *
  */
 struct inode *new_inode(struct super_block *sb)
 {
@@ -553,6 +557,8 @@ struct inode *new_inode(struct super_block *sb)
 		inode->i_ino = ++last_ino;
 		inode->i_state = 0;
 		spin_unlock(&inode_lock);
+		inode->i_uid = current->cred->uid;
+		inode->i_gid = current->cred->gid;
 	}
 	return inode;
 }
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
index 9bf2f6c..cc89629 100644
--- a/fs/inotify_user.c
+++ b/fs/inotify_user.c
@@ -547,6 +547,10 @@ asmlinkage long sys_inotify_init(void)
 	struct file *filp;
 	int fd, ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	fd = get_unused_fd();
 	if (fd < 0)
 		return fd;
@@ -619,6 +623,10 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
 	int ret, fput_needed;
 	unsigned flags = 0;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	filp = fget_light(fd, &fput_needed);
 	if (unlikely(!filp))
 		return -EBADF;
@@ -660,6 +668,10 @@ asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
 	struct inotify_device *dev;
 	int ret, fput_needed;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	filp = fget_light(fd, &fput_needed);
 	if (unlikely(!filp))
 		return -EBADF;
diff --git a/fs/ioctl.c b/fs/ioctl.c
index c2a773e..7088480 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -157,9 +157,14 @@ int vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned lon
 asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
 	struct file * filp;
-	int error = -EBADF;
+	int error;
 	int fput_needed;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
+	error = -EBADF;
 	filp = fget_light(fd, &fput_needed);
 	if (!filp)
 		goto out;
diff --git a/fs/locks.c b/fs/locks.c
index c795eaa..a6767e0 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1341,7 +1341,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
 	struct inode *inode = dentry->d_inode;
 	int error, rdlease_count = 0, wrlease_count = 0;
 
-	if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE))
+	if ((current->cred->uid != inode->i_uid) && !capable(CAP_LEASE))
 		return -EACCES;
 	if (!S_ISREG(inode->i_mode))
 		return -EINVAL;
@@ -1559,6 +1559,10 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
 	int can_sleep, unlock;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	filp = fget(fd);
 	if (!filp)
diff --git a/fs/namei.c b/fs/namei.c
index a83160a..59fcae5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -185,7 +185,7 @@ int generic_permission(struct inode *inode, int mask,
 {
 	umode_t			mode = inode->i_mode;
 
-	if (current->fsuid == inode->i_uid)
+	if (current->cred->uid == inode->i_uid)
 		mode >>= 6;
 	else {
 		if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
@@ -437,7 +437,7 @@ static int exec_permission_lite(struct inode *inode,
 	if (inode->i_op && inode->i_op->permission)
 		return -EAGAIN;
 
-	if (current->fsuid == inode->i_uid)
+	if (current->cred->uid == inode->i_uid)
 		mode >>= 6;
 	else if (in_group_p(inode->i_gid))
 		mode >>= 3;
@@ -1406,9 +1406,9 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
 {
 	if (!(dir->i_mode & S_ISVTX))
 		return 0;
-	if (inode->i_uid == current->fsuid)
+	if (inode->i_uid == current->cred->uid)
 		return 0;
-	if (dir->i_uid == current->fsuid)
+	if (dir->i_uid == current->cred->uid)
 		return 0;
 	return !capable(CAP_FOWNER);
 }
@@ -1914,11 +1914,15 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
 				unsigned dev)
 {
-	int error = 0;
+	int error;
 	char * tmp;
 	struct dentry * dentry;
 	struct nameidata nd;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	if (S_ISDIR(mode))
 		return -EPERM;
 	tmp = getname(filename);
@@ -1990,11 +1994,15 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
 asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
 {
-	int error = 0;
+	int error;
 	char * tmp;
 	struct dentry *dentry;
 	struct nameidata nd;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	tmp = getname(pathname);
 	error = PTR_ERR(tmp);
 	if (IS_ERR(tmp))
@@ -2088,11 +2096,15 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 
 static long do_rmdir(int dfd, const char __user *pathname)
 {
-	int error = 0;
+	int error;
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	name = getname(pathname);
 	if(IS_ERR(name))
 		return PTR_ERR(name);
@@ -2171,12 +2183,16 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
  */
 static long do_unlinkat(int dfd, const char __user *pathname)
 {
-	int error = 0;
+	int error;
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
 	struct inode *inode = NULL;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	name = getname(pathname);
 	if(IS_ERR(name))
 		return PTR_ERR(name);
@@ -2256,12 +2272,16 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
 asmlinkage long sys_symlinkat(const char __user *oldname,
 			      int newdfd, const char __user *newname)
 {
-	int error = 0;
+	int error;
 	char * from;
 	char * to;
 	struct dentry *dentry;
 	struct nameidata nd;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	from = getname(oldname);
 	if(IS_ERR(from))
 		return PTR_ERR(from);
@@ -2354,6 +2374,10 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
 	if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
 		return -EINVAL;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	to = getname(newname);
 	if (IS_ERR(to))
 		return PTR_ERR(to);
@@ -2541,12 +2565,16 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 static int do_rename(int olddfd, const char *oldname,
 			int newdfd, const char *newname)
 {
-	int error = 0;
+	int error;
 	struct dentry * old_dir, * new_dir;
 	struct dentry * old_dentry, *new_dentry;
 	struct dentry * trap;
 	struct nameidata oldnd, newnd;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
 	if (error)
 		goto exit;
diff --git a/fs/namespace.c b/fs/namespace.c
index ddbda13..62e2a07 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -633,6 +633,10 @@ asmlinkage long sys_umount(char __user * name, int flags)
 	struct nameidata nd;
 	int retval;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		goto out;
+
 	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
 	if (retval)
 		goto out;
@@ -1537,6 +1541,10 @@ asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name,
 	unsigned long dev_page;
 	char *dir_page;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	retval = copy_mount_options(type, &type_page);
 	if (retval < 0)
 		return retval;
@@ -1670,6 +1678,10 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
 	struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 71a49c3..c99f654 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -285,8 +285,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 			nfsi->change_attr = fattr->change_attr;
 		inode->i_size = nfs_size_to_loff_t(fattr->size);
 		inode->i_nlink = fattr->nlink;
-		inode->i_uid = fattr->uid;
-		inode->i_gid = fattr->gid;
 		if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) {
 			/*
 			 * report the blocks in 512byte units
diff --git a/fs/nfsctl.c b/fs/nfsctl.c
index 51f1b31..c70c880 100644
--- a/fs/nfsctl.c
+++ b/fs/nfsctl.c
@@ -90,6 +90,10 @@ asmlinkage sys_nfsservctl(int cmd, struct nfsctl_arg __user *arg, void __user *r
 	int version;
 	int err;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
 	if (copy_from_user(&version, &arg->ca_version, sizeof(int)))
 		return -EFAULT;
 
diff --git a/fs/open.c b/fs/open.c
index 1d9e5e9..0c05863 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -124,6 +124,10 @@ asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct statfs tmp;
@@ -143,6 +147,11 @@ asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64
 
 	if (sz != sizeof(*buf))
 		return -EINVAL;
+
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct statfs64 tmp;
@@ -161,6 +170,10 @@ asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf)
 	struct statfs tmp;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
@@ -182,6 +195,10 @@ asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user
 	if (sz != sizeof(*buf))
 		return -EINVAL;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
@@ -226,6 +243,10 @@ static long do_sys_truncate(const char __user * path, loff_t length)
 	struct inode * inode;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EINVAL;
 	if (length < 0)	/* sorry, but loff_t says... */
 		goto out;
@@ -295,6 +316,10 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 	struct file * file;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EINVAL;
 	if (length < 0)
 		goto out;
@@ -364,6 +389,10 @@ asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len)
 	if (offset < 0 || len <= 0)
 		goto out;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		goto out;
+
 	/* Return error if mode is not supported */
 	ret = -EOPNOTSUPP;
 	if (mode && !(mode & FALLOC_FL_KEEP_SIZE))
@@ -421,19 +450,30 @@ out:
 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 {
 	struct nameidata nd;
-	int old_fsuid, old_fsgid;
 	kernel_cap_t old_cap;
+	struct cred *cred;
 	int res;
 
 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
 		return -EINVAL;
 
-	old_fsuid = current->fsuid;
-	old_fsgid = current->fsgid;
+	res = update_current_cred();
+	if (res < 0)
+		return res;
+
 	old_cap = current->cap_effective;
 
-	current->fsuid = current->uid;
-	current->fsgid = current->gid;
+	if (current->cred->uid != current->uid ||
+	    current->cred->gid != current->gid) {
+		cred = dup_cred(current->cred);
+		if (!cred)
+			return -ENOMEM;
+
+		change_fsuid(cred, current->uid);
+		change_fsgid(cred, current->gid);
+	} else {
+		cred = get_current_cred();
+	}
 
 	/*
 	 * Clear the capabilities if we switch to a non-root user
@@ -448,6 +488,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 	else
 		current->cap_effective = current->cap_permitted;
 
+	cred = __set_current_cred(cred);
 	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
 	if (res)
 		goto out;
@@ -464,8 +505,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 out_path_release:
 	path_release(&nd);
 out:
-	current->fsuid = old_fsuid;
-	current->fsgid = old_fsgid;
+	set_current_cred(cred);
 	current->cap_effective = old_cap;
 
 	return res;
@@ -481,6 +521,10 @@ asmlinkage long sys_chdir(const char __user * filename)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = __user_walk(filename,
 			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
 	if (error)
@@ -506,6 +550,10 @@ asmlinkage long sys_fchdir(unsigned int fd)
 	struct vfsmount *mnt;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
@@ -533,6 +581,10 @@ asmlinkage long sys_chroot(const char __user * filename)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
 	if (error)
 		goto out;
@@ -559,9 +611,14 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
 	struct inode * inode;
 	struct dentry * dentry;
 	struct file * file;
-	int err = -EBADF;
+	int err;
 	struct iattr newattrs;
 
+	err = update_current_cred();
+	if (err < 0)
+		goto out;
+
+	err = -EBADF;
 	file = fget(fd);
 	if (!file)
 		goto out;
@@ -599,6 +656,10 @@ asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
 	int error;
 	struct iattr newattrs;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
 	if (error)
 		goto out;
@@ -671,6 +732,10 @@ asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = user_path_walk(filename, &nd);
 	if (error)
 		goto out;
@@ -690,6 +755,10 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
 	if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
 		goto out;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
 	error = __user_walk_fd(dfd, filename, follow, &nd);
 	if (error)
@@ -705,6 +774,10 @@ asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = user_path_walk_link(filename, &nd);
 	if (error)
 		goto out;
@@ -718,9 +791,14 @@ out:
 asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
 {
 	struct file * file;
-	int error = -EBADF;
+	int error;
 	struct dentry * dentry;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
+	error = -EBADF;
 	file = fget(fd);
 	if (!file)
 		goto out;
@@ -1026,6 +1104,11 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
 {
 	char *tmp = getname(filename);
 	int fd = PTR_ERR(tmp);
+	int ret;
+
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
 
 	if (!IS_ERR(tmp)) {
 		fd = get_unused_fd_flags(flags);
@@ -1121,6 +1204,8 @@ asmlinkage long sys_close(unsigned int fd)
 	struct fdtable *fdt;
 	int retval;
 
+	update_current_cred();
+
 	spin_lock(&files->file_lock);
 	fdt = files_fdtable(files);
 	if (fd >= fdt->max_fds)
diff --git a/fs/pipe.c b/fs/pipe.c
index 6b3d91a..18bf2ce 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -939,8 +939,6 @@ static struct inode * get_pipe_inode(void)
 	 */
 	inode->i_state = I_DIRTY;
 	inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
-	inode->i_uid = current->fsuid;
-	inode->i_gid = current->fsgid;
 	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 
 	return inode;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index aec931e..b36c79f 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -217,11 +217,11 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
                 switch(pa->e_tag) {
                         case ACL_USER_OBJ:
 				/* (May have been checked already) */
-                                if (inode->i_uid == current->fsuid)
+				if (inode->i_uid == current->cred->uid)
                                         goto check_perm;
                                 break;
                         case ACL_USER:
-                                if (pa->e_id == current->fsuid)
+				if (pa->e_id == current->cred->uid)
                                         goto mask;
 				break;
                         case ACL_GROUP_OBJ:
diff --git a/fs/proc/array.c b/fs/proc/array.c
index ee4814d..dc2f83a 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -159,10 +159,12 @@ static inline const char *get_task_state(struct task_struct *tsk)
 static inline char *task_state(struct task_struct *p, char *buffer)
 {
 	struct group_info *group_info;
+	struct cred *cred;
 	int g;
 	struct fdtable *fdt = NULL;
 
 	rcu_read_lock();
+	cred = get_task_cred(p);
 	buffer += sprintf(buffer,
 		"State:\t%s\n"
 		"Tgid:\t%d\n"
@@ -175,8 +177,8 @@ static inline char *task_state(struct task_struct *p, char *buffer)
 		p->tgid, p->pid,
 		pid_alive(p) ? rcu_dereference(p->real_parent)->tgid : 0,
 		pid_alive(p) && p->ptrace ? rcu_dereference(p->parent)->pid : 0,
-		p->uid, p->euid, p->suid, p->fsuid,
-		p->gid, p->egid, p->sgid, p->fsgid);
+		p->uid, p->euid, p->suid, cred->uid,
+		p->gid, p->egid, p->sgid, cred->gid);
 
 	task_lock(p);
 	if (p->files)
@@ -186,14 +188,12 @@ static inline char *task_state(struct task_struct *p, char *buffer)
 		"Groups:\t",
 		fdt ? fdt->max_fds : 0);
 	rcu_read_unlock();
-
-	group_info = p->group_info;
-	get_group_info(group_info);
 	task_unlock(p);
 
+	group_info = cred->group_info;
 	for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++)
 		buffer += sprintf(buffer, "%d ", GROUP_AT(group_info, g));
-	put_group_info(group_info);
+	put_cred(cred);
 
 	buffer += sprintf(buffer, "\n");
 	return buffer;
diff --git a/fs/quota.c b/fs/quota.c
index 99b24b5..26983bc 100644
--- a/fs/quota.c
+++ b/fs/quota.c
@@ -369,6 +369,10 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t
 	struct super_block *sb = NULL;
 	int ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	cmds = cmd >> SUBCMDSHIFT;
 	type = cmd & SUBCMDMASK;
 
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index ef2b46d..8c92fe0 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -55,8 +55,6 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
 
 	if (inode) {
 		inode->i_mode = mode;
-		inode->i_uid = current->fsuid;
-		inode->i_gid = current->fsgid;
 		inode->i_blocks = 0;
 		inode->i_mapping->a_ops = &ramfs_aops;
 		inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
diff --git a/fs/read_write.c b/fs/read_write.c
index 507ddff..2c6aa3d 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -134,6 +134,10 @@ asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
 	struct file * file;
 	int fput_needed;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	retval = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (!file)
@@ -161,6 +165,10 @@ asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high,
 	loff_t offset;
 	int fput_needed;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	retval = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (!file)
@@ -357,9 +365,14 @@ static inline void file_pos_write(struct file *file, loff_t pos)
 asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 	int fput_needed;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
@@ -375,9 +388,14 @@ EXPORT_SYMBOL_GPL(sys_read);
 asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 	int fput_needed;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
@@ -393,12 +411,17 @@ asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf,
 			     size_t count, loff_t pos)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 	int fput_needed;
 
 	if (pos < 0)
 		return -EINVAL;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		ret = -ESPIPE;
@@ -414,12 +437,17 @@ asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char __user *buf,
 			      size_t count, loff_t pos)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 	int fput_needed;
 
 	if (pos < 0)
 		return -EINVAL;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		ret = -ESPIPE;
@@ -664,9 +692,14 @@ asmlinkage ssize_t
 sys_readv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 	int fput_needed;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
@@ -685,9 +718,14 @@ asmlinkage ssize_t
 sys_writev(unsigned long fd, const struct iovec __user *vec, unsigned long vlen)
 {
 	struct file *file;
-	ssize_t ret = -EBADF;
+	ssize_t ret;
 	int fput_needed;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
+	ret = -EBADF;
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
@@ -711,6 +749,10 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
 	ssize_t retval;
 	int fput_needed_in, fput_needed_out, fl;
 
+	retval = update_current_cred();
+	if (retval < 0)
+		return retval;
+
 	/*
 	 * Get input file, and verify that it is ok..
 	 */
diff --git a/fs/readdir.c b/fs/readdir.c
index efe52e6..57e6aa9 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -187,6 +187,10 @@ asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * diren
 	struct getdents_callback buf;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EFAULT;
 	if (!access_ok(VERIFY_WRITE, dirent, count))
 		goto out;
@@ -271,6 +275,10 @@ asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * d
 	struct getdents_callback64 buf;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EFAULT;
 	if (!access_ok(VERIFY_WRITE, dirent, count))
 		goto out;
diff --git a/fs/select.c b/fs/select.c
index 46dca31..e2357c8 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -661,6 +661,10 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
 	long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
 	struct poll_list *stack_pp = NULL;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
 	/* Do a sanity check on nfds ... */
 	if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
 		return -EINVAL;
diff --git a/fs/signalfd.c b/fs/signalfd.c
index a8e293d..9e0bb24 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -324,6 +324,10 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas
 	struct inode *inode;
 	struct signalfd_lockctx lk;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	if (sizemask != sizeof(sigset_t) ||
 	    copy_from_user(&sigmask, user_mask, sizeof(sigmask)))
 		return -EINVAL;
diff --git a/fs/splice.c b/fs/splice.c
index c010a72..ceb1f07 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1510,6 +1510,10 @@ asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov,
 	else if (unlikely(!nr_segs))
 		return 0;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	file = fget_light(fd, &fput);
 	if (file) {
@@ -1535,6 +1539,10 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
 	if (unlikely(!len))
 		return 0;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	in = fget_light(fd_in, &fput_in);
 	if (in) {
@@ -1752,6 +1760,10 @@ asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags)
 	if (unlikely(!len))
 		return 0;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = -EBADF;
 	in = fget_light(fdin, &fput_in);
 	if (in) {
diff --git a/fs/stat.c b/fs/stat.c
index 6851006..ea8e08d 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -60,6 +60,10 @@ int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd);
 	if (!error) {
 		error = vfs_getattr(nd.mnt, nd.dentry, stat);
@@ -80,6 +84,10 @@ int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = __user_walk_fd(dfd, name, 0, &nd);
 	if (!error) {
 		error = vfs_getattr(nd.mnt, nd.dentry, stat);
@@ -98,8 +106,13 @@ EXPORT_SYMBOL(vfs_lstat);
 int vfs_fstat(unsigned int fd, struct kstat *stat)
 {
 	struct file *f = fget(fd);
-	int error = -EBADF;
+	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
+	error = -EBADF;
 	if (f) {
 		error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat);
 		fput(f);
@@ -300,6 +313,10 @@ asmlinkage long sys_readlinkat(int dfd, const char __user *path,
 	if (bufsiz <= 0)
 		return -EINVAL;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = __user_walk_fd(dfd, path, 0, &nd);
 	if (!error) {
 		struct inode * inode = nd.dentry->d_inode;
diff --git a/fs/super.c b/fs/super.c
index fc8ebed..28e7370 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -540,6 +540,10 @@ asmlinkage long sys_ustat(unsigned dev, struct ustat __user * ubuf)
         struct kstatfs sbuf;
 	int err = -EINVAL;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
         s = user_get_super(new_decode_dev(dev));
         if (s == NULL)
                 goto out;
diff --git a/fs/sync.c b/fs/sync.c
index 7cd005e..d5e2d5f 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -108,8 +108,13 @@ out:
 static long __do_fsync(unsigned int fd, int datasync)
 {
 	struct file *file;
-	int ret = -EBADF;
+	int ret;
+
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
 
+	ret = -EBADF;
 	file = fget(fd);
 	if (file) {
 		ret = do_fsync(file, datasync);
@@ -183,6 +188,10 @@ asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes,
 	int fput_needed;
 	umode_t i_mode;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		goto out;
+
 	ret = -EINVAL;
 	if (flags & ~VALID_FLAGS)
 		goto out;
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 61983f3..3f95c7d 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -159,6 +159,10 @@ asmlinkage long sys_timerfd(int ufd, int clockid, int flags,
 	struct inode *inode;
 	struct itimerspec ktmr;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
 		return -EFAULT;
 
diff --git a/fs/utimes.c b/fs/utimes.c
index 682eb63..409e433 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -51,6 +51,10 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags
 	struct iattr newattrs;
 	struct file *f = NULL;
 
+	error = update_current_cred();
+	if (error < 0)
+		goto out;
+
 	error = -EINVAL;
 	if (flags & ~AT_SYMLINK_NOFOLLOW)
 		goto out;
diff --git a/fs/xattr.c b/fs/xattr.c
index a44fd92..3137912 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -232,6 +232,10 @@ sys_setxattr(char __user *path, char __user *name, void __user *value,
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
@@ -247,6 +251,10 @@ sys_lsetxattr(char __user *path, char __user *name, void __user *value,
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
@@ -261,8 +269,13 @@ sys_fsetxattr(int fd, char __user *name, void __user *value,
 {
 	struct file *f;
 	struct dentry *dentry;
-	int error = -EBADF;
+	int error;
+
+	error = update_current_cred();
+	if (error < 0)
+		return error;
 
+	error = -EBADF;
 	f = fget(fd);
 	if (!f)
 		return error;
@@ -317,6 +330,10 @@ sys_getxattr(char __user *path, char __user *name, void __user *value,
 	struct nameidata nd;
 	ssize_t error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
@@ -332,6 +349,10 @@ sys_lgetxattr(char __user *path, char __user *name, void __user *value,
 	struct nameidata nd;
 	ssize_t error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
@@ -344,8 +365,13 @@ asmlinkage ssize_t
 sys_fgetxattr(int fd, char __user *name, void __user *value, size_t size)
 {
 	struct file *f;
-	ssize_t error = -EBADF;
+	ssize_t error;
+
+	error = update_current_cred();
+	if (error < 0)
+		return error;
 
+	error = -EBADF;
 	f = fget(fd);
 	if (!f)
 		return error;
@@ -391,6 +417,10 @@ sys_listxattr(char __user *path, char __user *list, size_t size)
 	struct nameidata nd;
 	ssize_t error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
@@ -405,6 +435,10 @@ sys_llistxattr(char __user *path, char __user *list, size_t size)
 	struct nameidata nd;
 	ssize_t error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
@@ -417,8 +451,13 @@ asmlinkage ssize_t
 sys_flistxattr(int fd, char __user *list, size_t size)
 {
 	struct file *f;
-	ssize_t error = -EBADF;
+	ssize_t error;
+
+	error = update_current_cred();
+	if (error < 0)
+		return error;
 
+	error = -EBADF;
 	f = fget(fd);
 	if (!f)
 		return error;
@@ -452,6 +491,10 @@ sys_removexattr(char __user *path, char __user *name)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
@@ -466,6 +509,10 @@ sys_lremovexattr(char __user *path, char __user *name)
 	struct nameidata nd;
 	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
@@ -479,8 +526,13 @@ sys_fremovexattr(int fd, char __user *name)
 {
 	struct file *f;
 	struct dentry *dentry;
-	int error = -EBADF;
+	int error;
+
+	error = update_current_cred();
+	if (error < 0)
+		return error;
 
+	error = -EBADF;
 	f = fget(fd);
 	if (!f)
 		return error;
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 91c8c07..f20f057 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -39,6 +39,7 @@ struct linux_binprm{
 	int e_uid, e_gid;
 	kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
 	void *security;
+	struct cred *cred;
 	int argc, envc;
 	char * filename;	/* Name of binary as seen by procps */
 	char * interp;		/* Name of the binary really executed. Most
diff --git a/include/linux/cred.h b/include/linux/cred.h
new file mode 100644
index 0000000..22ae610
--- /dev/null
+++ b/include/linux/cred.h
@@ -0,0 +1,172 @@
+/* Credentials management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_CRED_H
+#define _LINUX_CRED_H
+
+#include <linux/rcupdate.h>
+
+#ifdef __KERNEL__
+
+/*
+ * credentials record
+ * - COW semantics apply
+ */
+struct cred {
+	atomic_t		usage;
+	uid_t			uid;		/* fsuid as was */
+	gid_t			gid;		/* fsgid as was */
+	struct rcu_head		exterminate;	/* cred destroyer */
+	struct group_info	*group_info;
+
+	/* caches for references to the three task keyrings
+	 * - note that key_ref_t isn't typedef'd at this point, hence the odd
+	 *   types
+	 */
+#ifdef CONFIG_KEYS
+	struct __key_reference_with_attributes *session_keyring;
+	struct __key_reference_with_attributes *process_keyring;
+	struct __key_reference_with_attributes *thread_keyring;
+#endif
+};
+
+extern struct cred init_cred;
+
+extern int update_current_cred(void);
+extern void put_cred(struct cred *);
+extern void change_fsuid(struct cred *, uid_t);
+extern void change_fsgid(struct cred *, gid_t);
+extern void change_group_info(struct cred *, struct group_info *);
+extern struct cred *dup_cred(const struct cred *);
+
+/**
+ * get_cred - Get an extra reference on a credentials record
+ * @cred: The credentials record to reference
+ *
+ * Get an extra reference on a credentials record.  This must be released by
+ * calling put_cred().
+ */
+static inline struct cred *get_cred(struct cred *cred)
+{
+	atomic_inc(&cred->usage);
+	return cred;
+}
+
+/**
+ * get_current_cred - Get an extra reference on the current's credentials record
+ *
+ * Get an extra reference on the credentials record attached to the current
+ * task.  This must be released by calling put_cred().
+ */
+#define get_current_cred() \
+	({ get_cred(current->cred); })
+
+/**
+ * task_cred - Access the credentials of another task
+ * @tsk: The task to access
+ *
+ * Get a pointer to the credentials record of the given task.  The caller must
+ * have done rcu_read_lock() first.  The credentials record is can only be
+ * accessed as long as the RCU readlock is held by the caller.  If the
+ * credentials are required for longer, then a reference should be obtained on
+ * the cred struct.
+ *
+ * This is not required for the a task to access its own credentials.  Tasks
+ * may not alter the credentials of other tasks.
+ */
+#define task_cred(tsk) \
+	({ rcu_dereference((tsk)->cred); })
+
+/**
+ * __task_fsuid - Get the FSUID of another task (caller holds RCU read lock)
+ * task_fsuid - Get the FSUID of another task
+ * @tsk: The task to access
+ *
+ * Get the active filesystem access UID of another task.  __task_fsuid()
+ * requires the caller to hold the RCU read lock, task_fsuid() does not.
+ */
+#define __task_fsuid(tsk)	(task_cred(tsk)->uid)
+#define task_fsuid(tsk)				\
+({						\
+	uid_t ____x;				\
+	rcu_read_lock();			\
+	____x = __task_fsuid(tsk);		\
+	rcu_read_unlock();			\
+	____x;					\
+})
+
+/**
+ * __task_fsgid - Get the FSGID of another task (caller holds RCU read lock)
+ * task_fsgid - Get the FSGID of another task
+ * @tsk: The task to access
+ *
+ * Get the active filesystem access GID of another task.  __task_fsgid()
+ * requires the caller to hold the RCU read lock, task_fsgid() does not.
+ */
+#define __task_fsgid(tsk)	(task_cred(tsk)->gid)
+#define task_fsgid(tsk)				\
+({						\
+	gid_t ____x;				\
+	rcu_read_lock();			\
+	____x = __task_fsgid(tsk);		\
+	rcu_read_unlock();			\
+	____x;					\
+})
+
+/**
+ * get_task_cred - Get an extra reference on a credentials record of a task
+ * @tsk: The task to look in
+ *
+ * Get an extra reference on a credentials record of the given task and return
+ * a pointer to it.  This must be released by calling put_cred().  The caller
+ * must have done rcu_read_lock() first.
+ */
+#define get_task_cred(tsk) \
+	({ get_cred(task_cred((tsk))); })
+
+/**
+ * __set_current_cred - Swap the current credentials on the current task
+ * @cred: The revised credentials
+ *
+ * Exchange the credential record of the current task for an updated one.  This
+ * transfers a reference on the passed credential to the current task_struct,
+ * so the caller may need to get an extra reference first.  The old credentials
+ * are returned and must be disposed of appropriately.
+ *
+ * Write-locking is achieved by the fact that a thread's credentials may only
+ * be changed by that thread itself, so no explicit locking is required.
+ */
+#define __set_current_cred(CRED)			\
+({							\
+	struct cred *___old = current->cred;		\
+	rcu_assign_pointer(current->cred, (CRED));	\
+	___old;						\
+})
+
+/**
+ * set_current_cred - Change the current credentials on the current task
+ * @cred: The revised credentials
+ *
+ * Exchange the credential record of the current task for an updated one.  This
+ * transfers a reference on the passed credential to the current task_struct,
+ * so the caller may need to get an extra reference first.  The old credentials
+ * are released.
+ *
+ * Write-locking is achieved by the fact that a thread's credentials may only
+ * be changed by that thread itself, so no explicit locking is required.
+ */
+#define set_current_cred(CRED)				\
+do {							\
+	put_cred(__set_current_cred(CRED));		\
+} while (0)
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_CRED_H */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 16421f6..bf35441 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -285,6 +285,7 @@ extern int dir_notify_enable;
 #include <linux/mutex.h>
 #include <linux/sysctl.h>
 #include <linux/capability.h>
+#include <linux/cred.h>
 
 #include <asm/atomic.h>
 #include <asm/semaphore.h>
@@ -736,7 +737,7 @@ struct file {
 	mode_t			f_mode;
 	loff_t			f_pos;
 	struct fown_struct	f_owner;
-	unsigned int		f_uid, f_gid;
+	struct cred		*f_cred;
 	struct file_ra_state	f_ra;
 
 	unsigned long		f_version;
@@ -999,7 +1000,7 @@ enum {
 #define has_fs_excl() atomic_read(&current->fs_excl)
 
 #define is_owner_or_cap(inode)	\
-	((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER))
+	((current->cred->uid == (inode)->i_uid) || capable(CAP_FOWNER))
 
 /* not quite ready to be deprecated, but... */
 extern void lock_super(struct super_block *);
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index cab741c..a1882e6 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -89,8 +89,6 @@ extern struct nsproxy init_nsproxy;
 	.signalfd_list	= LIST_HEAD_INIT(sighand.signalfd_list),	\
 }
 
-extern struct group_info init_groups;
-
 #define INIT_STRUCT_PID {						\
 	.count 		= ATOMIC_INIT(1),				\
 	.nr		= 0, 						\
@@ -142,7 +140,7 @@ extern struct group_info init_groups;
 	.children	= LIST_HEAD_INIT(tsk.children),			\
 	.sibling	= LIST_HEAD_INIT(tsk.sibling),			\
 	.group_leader	= &tsk,						\
-	.group_info	= &init_groups,					\
+	.cred		= &init_cred,					\
 	.cap_effective	= CAP_INIT_EFF_SET,				\
 	.cap_inheritable = CAP_INIT_INH_SET,				\
 	.cap_permitted	= CAP_FULL_SET,					\
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f4e324e..ea85955 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -79,6 +79,7 @@ struct sched_param {
 #include <linux/rcupdate.h>
 #include <linux/futex.h>
 #include <linux/rtmutex.h>
+#include <linux/cred.h>
 
 #include <linux/time.h>
 #include <linux/param.h>
@@ -1033,9 +1034,9 @@ struct task_struct {
 	struct list_head cpu_timers[3];
 
 /* process credentials */
-	uid_t uid,euid,suid,fsuid;
-	gid_t gid,egid,sgid,fsgid;
-	struct group_info *group_info;
+	struct cred *cred;
+	uid_t uid,euid,suid;
+	gid_t gid,egid,sgid;
 	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
 	unsigned keep_capabilities:1;
 	struct user_struct *user;
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index 7a69ca3..c06891d 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -21,13 +21,6 @@
 /* size of the nodename buffer */
 #define UNX_MAXNODENAME	32
 
-/* Work around the lack of a VFS credential */
-struct auth_cred {
-	uid_t	uid;
-	gid_t	gid;
-	struct group_info *group_info;
-};
-
 /*
  * Client user credentials
  */
@@ -103,8 +96,8 @@ struct rpc_authops {
 	struct rpc_auth *	(*create)(struct rpc_clnt *, rpc_authflavor_t);
 	void			(*destroy)(struct rpc_auth *);
 
-	struct rpc_cred *	(*lookup_cred)(struct rpc_auth *, struct auth_cred *, int);
-	struct rpc_cred *	(*crcreate)(struct rpc_auth*, struct auth_cred *, int);
+	struct rpc_cred *	(*lookup_cred)(struct rpc_auth *, struct cred *, int);
+	struct rpc_cred *	(*crcreate)(struct rpc_auth*, struct cred *, int);
 };
 
 struct rpc_credops {
@@ -112,7 +105,7 @@ struct rpc_credops {
 	int			(*cr_init)(struct rpc_auth *, struct rpc_cred *);
 	void			(*crdestroy)(struct rpc_cred *);
 
-	int			(*crmatch)(struct auth_cred *, struct rpc_cred *, int);
+	int			(*crmatch)(struct cred *, struct rpc_cred *, int);
 	__be32 *		(*crmarshal)(struct rpc_task *, __be32 *);
 	int			(*crrefresh)(struct rpc_task *);
 	__be32 *		(*crvalidate)(struct rpc_task *, __be32 *);
@@ -133,8 +126,8 @@ int			rpcauth_register(const struct rpc_authops *);
 int			rpcauth_unregister(const struct rpc_authops *);
 struct rpc_auth *	rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
 void			rpcauth_release(struct rpc_auth *);
-struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
-void			rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
+struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct cred *, int);
+void			rpcauth_init_cred(struct rpc_cred *, const struct cred *, struct rpc_auth *, const struct rpc_credops *);
 struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
 struct rpc_cred *	rpcauth_bindcred(struct rpc_task *);
 void			rpcauth_holdcred(struct rpc_task *);
diff --git a/kernel/Makefile b/kernel/Makefile
index 2a99983..1f1f17b 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y     = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
 	    rcupdate.o extable.o params.o posix-timers.o \
 	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
 	    hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \
-	    utsname.o
+	    utsname.o cred.o
 
 obj-$(CONFIG_STACKTRACE) += stacktrace.o
 obj-y += time/
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 04f3ffb..282e041 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -303,7 +303,8 @@ static int audit_filter_rules(struct task_struct *tsk,
 			result = audit_comparator(tsk->suid, f->op, f->val);
 			break;
 		case AUDIT_FSUID:
-			result = audit_comparator(tsk->fsuid, f->op, f->val);
+			result = audit_comparator(task_fsuid(tsk), f->op,
+						  f->val);
 			break;
 		case AUDIT_GID:
 			result = audit_comparator(tsk->gid, f->op, f->val);
@@ -315,7 +316,8 @@ static int audit_filter_rules(struct task_struct *tsk,
 			result = audit_comparator(tsk->sgid, f->op, f->val);
 			break;
 		case AUDIT_FSGID:
-			result = audit_comparator(tsk->fsgid, f->op, f->val);
+			result = audit_comparator(task_fsgid(tsk), f->op,
+						  f->val);
 			break;
 		case AUDIT_PERS:
 			result = audit_comparator(tsk->personality, f->op, f->val);
@@ -885,12 +887,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 	context->gid = tsk->gid;
 	context->euid = tsk->euid;
 	context->suid = tsk->suid;
-	context->fsuid = tsk->fsuid;
 	context->egid = tsk->egid;
 	context->sgid = tsk->sgid;
-	context->fsgid = tsk->fsgid;
 	context->personality = tsk->personality;
 
+	rcu_read_lock();
+	context->fsuid = __task_fsuid(tsk);
+	context->fsgid = __task_fsgid(tsk);
+	rcu_read_unlock();
+
 	ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
 	if (!ab)
 		return;		/* audit_panic has been called */
diff --git a/kernel/cred.c b/kernel/cred.c
new file mode 100644
index 0000000..e96dafe
--- /dev/null
+++ b/kernel/cred.c
@@ -0,0 +1,161 @@
+/* Credential caching/management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/key.h>
+#include "kernel-int.h"
+
+/*
+ * the credentials for the init task
+ * - the usage count is elevated so that this is never freed
+ */
+struct cred init_cred = {
+	.usage		= ATOMIC_INIT(2),
+	.group_info	= &init_groups,
+};
+
+/**
+ * update_current_cred - Bring the current task's creds up to date
+ *
+ * Bring the current task's credentials up to date with respect to the keyrings
+ * they shadow.  The process and session level keyrings may get changed by
+ * sibling threads with the same process, but the change can't be applied back
+ * to this thread's cred struct except by this thread itself.
+ */
+int update_current_cred(void)
+{
+	struct signal_struct *sig = current->signal;
+	struct cred *cred = current->cred;
+	struct key *keyring;
+
+	if (
+#ifdef CONFIG_KEYS
+		key_ref_to_ptr(cred->session_keyring) == sig->session_keyring &&
+		key_ref_to_ptr(cred->process_keyring) == sig->process_keyring &&
+		key_ref_to_ptr(cred->thread_keyring) == current->thread_keyring &&
+#endif
+		true)
+		return 0;
+
+	cred = kmalloc(sizeof(struct cred), GFP_KERNEL);
+	if (!cred)
+		return -ENOMEM;
+
+	*cred = *current->cred;
+	atomic_set(&cred->usage, 1);
+	get_group_info(cred->group_info);
+
+#ifdef CONFIG_KEYS
+	rcu_read_lock();
+	keyring = key_get(rcu_dereference(sig->session_keyring));
+	rcu_read_unlock();
+	if (!keyring)
+		keyring = key_get(current->user->session_keyring);
+	cred->session_keyring = make_key_ref(keyring, 1);
+
+	keyring = key_get(sig->process_keyring);
+	if (keyring)
+		cred->process_keyring = make_key_ref(keyring, 1);
+	else
+		cred->process_keyring = NULL;
+
+	cred->thread_keyring = NULL;
+#endif
+
+	set_current_cred(cred);
+	return 0;
+}
+
+/**
+ * dup_cred - Duplicate a credentials structure
+ * @pcred: The credentials record to duplicate
+ *
+ * Duplicate and return a credentials structure so that the copy can be
+ * modified.  NULL is returned if there is insufficient memory to make the
+ * copy.
+ */
+struct cred *dup_cred(const struct cred *pcred)
+{
+	struct cred *cred;
+
+	cred = kmalloc(sizeof(struct cred), GFP_KERNEL);
+	if (likely(cred)) {
+		*cred = *pcred;
+		atomic_set(&cred->usage, 1);
+		get_group_info(cred->group_info);
+		key_get(key_ref_to_ptr(cred->session_keyring));
+		key_get(key_ref_to_ptr(cred->process_keyring));
+		key_get(key_ref_to_ptr(cred->thread_keyring));
+	}
+	return cred;
+}
+
+EXPORT_SYMBOL(dup_cred);
+
+/*
+ * RCU-based credentials destroyer
+ */
+static void put_cred_rcu(struct rcu_head *rcu)
+{
+	struct cred *cred = container_of(rcu, struct cred, exterminate);
+
+	put_group_info(cred->group_info);
+	key_ref_put(cred->session_keyring);
+	key_ref_put(cred->process_keyring);
+	key_ref_put(cred->thread_keyring);
+	kfree(cred);
+}
+
+/**
+ * put_cred - Release a reference to a credentials record
+ * cred: The credentials record to release
+ *
+ * Release a reference to a credentials record.  When the last reference is
+ * released, the record will be deleted with due care for RCU accesses still
+ * ongoing.
+ */
+void put_cred(struct cred *cred)
+{
+	if (atomic_dec_and_test(&cred->usage))
+		call_rcu(&cred->exterminate, put_cred_rcu);
+}
+
+EXPORT_SYMBOL(put_cred);
+
+/*
+ * change the VFS applicable UID in a new credential record
+ */
+void change_fsuid(struct cred *cred, uid_t uid)
+{
+	cred->uid = uid;
+}
+
+/*
+ * change the VFS applicable GID in a new credential record
+ */
+void change_fsgid(struct cred *cred, gid_t gid)
+{
+	cred->gid = gid;
+}
+
+/*
+ * change the groups list in a new credential record
+ */
+void change_group_info(struct cred *cred, struct group_info *group_info)
+{
+	struct group_info *old = cred->group_info;
+
+	get_group_info(group_info);
+	cred->group_info = group_info;
+	put_group_info(old);
+}
diff --git a/kernel/exit.c b/kernel/exit.c
index 06b24b3..d1aa7fb 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -297,6 +297,7 @@ static void reparent_to_kthreadd(void)
 	/* cpus_allowed? */
 	/* rt_priority? */
 	/* signals? */
+	set_current_cred(get_cred(&init_cred));
 	security_task_reparent_to_init(current);
 	memcpy(current->signal->rlim, init_task.signal->rlim,
 	       sizeof(current->signal->rlim));
diff --git a/kernel/fork.c b/kernel/fork.c
index 7332e23..40f8cda 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -121,7 +121,7 @@ void __put_task_struct(struct task_struct *tsk)
 
 	security_task_free(tsk);
 	free_uid(tsk->user);
-	put_group_info(tsk->group_info);
+	put_cred(tsk->cred);
 	delayacct_tsk_free(tsk);
 
 	if (!profile_handoff_task(tsk))
@@ -949,6 +949,57 @@ static inline void rt_mutex_init_task(struct task_struct *p)
 }
 
 /*
+ * Copy a set of credentials
+ */
+static int copy_cred(struct task_struct *p)
+{
+	struct cred *cred;
+#ifdef CONFIG_KEYS
+	struct key *session_keyring, *process_keyring;
+#endif
+
+	/* share the credentials if we can */
+	if (
+#ifdef CONFIG_KEYS
+		!p->cred->thread_keyring &&
+#endif
+		true
+	    ) {
+		atomic_inc(&p->cred->usage);
+	} else {
+		cred = kmalloc(sizeof(struct cred), GFP_KERNEL);
+		if (!cred)
+			return -ENOMEM;
+
+		*cred = *p->cred;
+		atomic_set(&cred->usage, 1);
+		get_group_info(cred->group_info);
+
+#ifdef CONFIG_KEYS
+		rcu_read_lock();
+		session_keyring =
+			key_get(rcu_dereference(p->signal->session_keyring));
+		rcu_read_unlock();
+		if (!session_keyring)
+			session_keyring = key_get(p->user->session_keyring);
+		cred->session_keyring = make_key_ref(session_keyring, 1);
+
+		process_keyring = key_get(p->signal->process_keyring);
+		if (process_keyring)
+			cred->process_keyring =
+				make_key_ref(process_keyring, 1);
+		else
+			cred->process_keyring = NULL;
+
+		cred->thread_keyring = NULL;
+#endif
+
+		p->cred = cred;
+	}
+	return 0;
+}
+
+/*
  * This creates a new process as a copy of the old one,
  * but does not actually start it yet.
  *
@@ -1010,7 +1061,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 
 	atomic_inc(&p->user->__count);
 	atomic_inc(&p->user->processes);
-	get_group_info(p->group_info);
+	if ((retval = copy_cred(p) < 0))
+		goto bad_fork_cleanup_count;
 
 	/*
 	 * If multiple threads are within copy_process(), then this check
@@ -1018,10 +1070,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	 * to stop root fork bombs.
 	 */
 	if (nr_threads >= max_threads)
-		goto bad_fork_cleanup_count;
+		goto bad_fork_cleanup_cred;
 
 	if (!try_module_get(task_thread_info(p)->exec_domain->module))
-		goto bad_fork_cleanup_count;
+		goto bad_fork_cleanup_cred;
 
 	if (p->binfmt && !try_module_get(p->binfmt->module))
 		goto bad_fork_cleanup_put_domain;
@@ -1309,8 +1361,9 @@ bad_fork_cleanup_delays_binfmt:
 		module_put(p->binfmt->module);
 bad_fork_cleanup_put_domain:
 	module_put(task_thread_info(p)->exec_domain->module);
+bad_fork_cleanup_cred:
+	put_cred(p->cred);
 bad_fork_cleanup_count:
-	put_group_info(p->group_info);
 	atomic_dec(&p->user->processes);
 	free_uid(p->user);
 bad_fork_free:
diff --git a/kernel/sys.c b/kernel/sys.c
index 1b33b05..7f15e9f 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -41,6 +41,7 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/unistd.h>
+#include "kernel-int.h"
 
 #ifndef SET_UNALIGN_CTL
 # define SET_UNALIGN_CTL(a,b)	(-EINVAL)
@@ -1011,6 +1012,7 @@ void ctrl_alt_del(void)
  */
 asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
 {
+	struct cred *cred;
 	int old_rgid = current->gid;
 	int old_egid = current->egid;
 	int new_rgid = old_rgid;
@@ -1038,6 +1040,11 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
 		else
 			return -EPERM;
 	}
+
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	if (new_egid != old_egid) {
 		set_dumpable(current->mm, suid_dumpable);
 		smp_wmb();
@@ -1045,9 +1052,10 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
 	if (rgid != (gid_t) -1 ||
 	    (egid != (gid_t) -1 && egid != old_rgid))
 		current->sgid = new_egid;
-	current->fsgid = new_egid;
 	current->egid = new_egid;
 	current->gid = new_rgid;
+	change_fsgid(cred, new_egid);
+	set_current_cred(cred);
 	key_fsgid_changed(current);
 	proc_id_connector(current, PROC_EVENT_GID);
 	return 0;
@@ -1060,6 +1068,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
  */
 asmlinkage long sys_setgid(gid_t gid)
 {
+	struct cred *cred;
 	int old_egid = current->egid;
 	int retval;
 
@@ -1067,22 +1076,29 @@ asmlinkage long sys_setgid(gid_t gid)
 	if (retval)
 		return retval;
 
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	if (capable(CAP_SETGID)) {
 		if (old_egid != gid) {
 			set_dumpable(current->mm, suid_dumpable);
 			smp_wmb();
 		}
-		current->gid = current->egid = current->sgid = current->fsgid = gid;
+		current->gid = current->egid = current->sgid = gid;
 	} else if ((gid == current->gid) || (gid == current->sgid)) {
 		if (old_egid != gid) {
 			set_dumpable(current->mm, suid_dumpable);
 			smp_wmb();
 		}
-		current->egid = current->fsgid = gid;
-	}
-	else
+		current->egid = gid;
+	} else {
+		put_cred(cred);
 		return -EPERM;
+	}
 
+	change_fsgid(cred, gid);
+	set_current_cred(cred);
 	key_fsgid_changed(current);
 	proc_id_connector(current, PROC_EVENT_GID);
 	return 0;
@@ -1130,6 +1146,7 @@ static int set_user(uid_t new_ruid, int dumpclear)
  */
 asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
 {
+	struct cred *cred;
 	int old_ruid, old_euid, old_suid, new_ruid, new_euid;
 	int retval;
 
@@ -1158,19 +1175,26 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
 			return -EPERM;
 	}
 
-	if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
+	if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) {
+		put_cred(cred);
 		return -EAGAIN;
+	}
 
 	if (new_euid != old_euid) {
 		set_dumpable(current->mm, suid_dumpable);
 		smp_wmb();
 	}
-	current->fsuid = current->euid = new_euid;
+	current->euid = new_euid;
 	if (ruid != (uid_t) -1 ||
 	    (euid != (uid_t) -1 && euid != old_ruid))
 		current->suid = current->euid;
-	current->fsuid = current->euid;
 
+	change_fsuid(cred, new_euid);
+	set_current_cred(cred);
 	key_fsuid_changed(current);
 	proc_id_connector(current, PROC_EVENT_UID);
 
@@ -1192,6 +1216,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
  */
 asmlinkage long sys_setuid(uid_t uid)
 {
+	struct cred *cred;
 	int old_euid = current->euid;
 	int old_ruid, old_suid, new_suid;
 	int retval;
@@ -1200,24 +1225,33 @@ asmlinkage long sys_setuid(uid_t uid)
 	if (retval)
 		return retval;
 
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	old_ruid = current->uid;
 	old_suid = current->suid;
 	new_suid = old_suid;
 	
 	if (capable(CAP_SETUID)) {
-		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
+		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) {
+			put_cred(cred);
 			return -EAGAIN;
+		}
 		new_suid = uid;
-	} else if ((uid != current->uid) && (uid != new_suid))
+	} else if ((uid != current->uid) && (uid != new_suid)) {
+		put_cred(cred);
 		return -EPERM;
+	}
 
 	if (old_euid != uid) {
 		set_dumpable(current->mm, suid_dumpable);
 		smp_wmb();
 	}
-	current->fsuid = current->euid = uid;
+	current->euid = uid;
 	current->suid = new_suid;
-
+	change_fsuid(cred, uid);
+	set_current_cred(cred);
 	key_fsuid_changed(current);
 	proc_id_connector(current, PROC_EVENT_UID);
 
@@ -1231,6 +1265,7 @@ asmlinkage long sys_setuid(uid_t uid)
  */
 asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 {
+	struct cred *cred;
 	int old_ruid = current->uid;
 	int old_euid = current->euid;
 	int old_suid = current->suid;
@@ -1251,9 +1286,16 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 		    (suid != current->euid) && (suid != current->suid))
 			return -EPERM;
 	}
+
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	if (ruid != (uid_t) -1) {
-		if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
+		if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0) {
+			put_cred(cred);
 			return -EAGAIN;
+		}
 	}
 	if (euid != (uid_t) -1) {
 		if (euid != current->euid) {
@@ -1262,10 +1304,10 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 		}
 		current->euid = euid;
 	}
-	current->fsuid = current->euid;
 	if (suid != (uid_t) -1)
 		current->suid = suid;
-
+	change_fsuid(cred, current->euid);
+	set_current_cred(cred);
 	key_fsuid_changed(current);
 	proc_id_connector(current, PROC_EVENT_UID);
 
@@ -1288,6 +1330,7 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us
  */
 asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 {
+	struct cred *cred;
 	int retval;
 
 	retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
@@ -1305,6 +1348,11 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 		    (sgid != current->egid) && (sgid != current->sgid))
 			return -EPERM;
 	}
+
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	if (egid != (gid_t) -1) {
 		if (egid != current->egid) {
 			set_dumpable(current->mm, suid_dumpable);
@@ -1312,12 +1360,13 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 		}
 		current->egid = egid;
 	}
-	current->fsgid = current->egid;
 	if (rgid != (gid_t) -1)
 		current->gid = rgid;
 	if (sgid != (gid_t) -1)
 		current->sgid = sgid;
 
+	change_fsgid(cred, current->egid);
+	set_current_cred(cred);
 	key_fsgid_changed(current);
 	proc_id_connector(current, PROC_EVENT_GID);
 	return 0;
@@ -1343,23 +1392,31 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us
  */
 asmlinkage long sys_setfsuid(uid_t uid)
 {
+	struct cred *cred;
 	int old_fsuid;
 
-	old_fsuid = current->fsuid;
+	old_fsuid = current->cred->uid;
 	if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS))
 		return old_fsuid;
 
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	if (uid == current->uid || uid == current->euid ||
-	    uid == current->suid || uid == current->fsuid || 
+	    uid == current->suid || uid == current->cred->uid ||
 	    capable(CAP_SETUID)) {
 		if (uid != old_fsuid) {
 			set_dumpable(current->mm, suid_dumpable);
 			smp_wmb();
 		}
-		current->fsuid = uid;
+		change_fsuid(cred, uid);
+		set_current_cred(cred);
+		key_fsuid_changed(current);
+	} else {
+		put_cred(cred);
 	}
 
-	key_fsuid_changed(current);
 	proc_id_connector(current, PROC_EVENT_UID);
 
 	security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
@@ -1372,22 +1429,30 @@ asmlinkage long sys_setfsuid(uid_t uid)
  */
 asmlinkage long sys_setfsgid(gid_t gid)
 {
+	struct cred *cred;
 	int old_fsgid;
 
-	old_fsgid = current->fsgid;
+	old_fsgid = current->cred->gid;
 	if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
 		return old_fsgid;
 
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
+
 	if (gid == current->gid || gid == current->egid ||
-	    gid == current->sgid || gid == current->fsgid || 
+	    gid == current->sgid || gid == current->cred->gid ||
 	    capable(CAP_SETGID)) {
 		if (gid != old_fsgid) {
 			set_dumpable(current->mm, suid_dumpable);
 			smp_wmb();
 		}
-		current->fsgid = gid;
+		change_fsgid(cred, gid);
+		set_current_cred(cred);
 		key_fsgid_changed(current);
 		proc_id_connector(current, PROC_EVENT_GID);
+	} else {
+		put_cred(cred);
 	}
 	return old_fsgid;
 }
@@ -1754,23 +1819,20 @@ int groups_search(struct group_info *group_info, gid_t grp)
 /* validate and set current->group_info */
 int set_current_groups(struct group_info *group_info)
 {
+	struct cred *cred;
 	int retval;
-	struct group_info *old_info;
 
 	retval = security_task_setgroups(group_info);
 	if (retval)
 		return retval;
 
-	groups_sort(group_info);
-	get_group_info(group_info);
-
-	task_lock(current);
-	old_info = current->group_info;
-	current->group_info = group_info;
-	task_unlock(current);
-
-	put_group_info(old_info);
+	cred = dup_cred(current->cred);
+	if (!cred)
+		return -ENOMEM;
 
+	groups_sort(group_info);
+	change_group_info(cred, group_info);
+	set_current_cred(cred);
 	return 0;
 }
 
@@ -1778,24 +1840,24 @@ EXPORT_SYMBOL(set_current_groups);
 
 asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
 {
+	struct group_info *group_info = current->cred->group_info;
 	int i = 0;
 
 	/*
-	 *	SMP: Nobody else can change our grouplist. Thus we are
-	 *	safe.
+	 *	SMP: Nobody else can change our credentials.  Thus we are safe.
 	 */
 
 	if (gidsetsize < 0)
 		return -EINVAL;
 
 	/* no need to grab task_lock here; it cannot change */
-	i = current->group_info->ngroups;
+	i = group_info->ngroups;
 	if (gidsetsize) {
 		if (i > gidsetsize) {
 			i = -EINVAL;
 			goto out;
 		}
-		if (groups_to_user(grouplist, current->group_info)) {
+		if (groups_to_user(grouplist, group_info)) {
 			i = -EFAULT;
 			goto out;
 		}
@@ -1839,9 +1901,11 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
  */
 int in_group_p(gid_t grp)
 {
+	struct cred *cred = current->cred;
 	int retval = 1;
-	if (grp != current->fsgid)
-		retval = groups_search(current->group_info, grp);
+
+	if (grp != cred->gid)
+		retval = groups_search(cred->group_info, grp);
 	return retval;
 }
 
@@ -1851,7 +1915,7 @@ int in_egroup_p(gid_t grp)
 {
 	int retval = 1;
 	if (grp != current->egid)
-		retval = groups_search(current->group_info, grp);
+		retval = groups_search(current->cred->group_info, grp);
 	return retval;
 }
 
diff --git a/kernel/uid16.c b/kernel/uid16.c
index dd308ba..5a8b95e 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -161,25 +161,24 @@ static int groups16_from_user(struct group_info *group_info,
 
 asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist)
 {
+	struct group_info *group_info = current->cred->group_info;
 	int i = 0;
 
 	if (gidsetsize < 0)
 		return -EINVAL;
 
-	get_group_info(current->group_info);
-	i = current->group_info->ngroups;
+	i = group_info->ngroups;
 	if (gidsetsize) {
 		if (i > gidsetsize) {
 			i = -EINVAL;
 			goto out;
 		}
-		if (groups16_to_user(grouplist, current->group_info)) {
+		if (groups16_to_user(grouplist, group_info)) {
 			i = -EFAULT;
 			goto out;
 		}
 	}
 out:
-	put_group_info(current->group_info);
 	return i;
 }
 
diff --git a/mm/fadvise.c b/mm/fadvise.c
index 0df4c89..db3d602 100644
--- a/mm/fadvise.c
+++ b/mm/fadvise.c
@@ -35,6 +35,10 @@ asmlinkage long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice)
 	unsigned long nrpages;
 	int ret = 0;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	if (!file)
 		return -EBADF;
 
diff --git a/mm/filemap.c b/mm/filemap.c
index 90b657b..3257be2 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1235,6 +1235,10 @@ asmlinkage ssize_t sys_readahead(int fd, loff_t offset, size_t count)
 	ssize_t ret;
 	struct file *file;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	ret = -EBADF;
 	file = fget(fd);
 	if (file) {
diff --git a/mm/fremap.c b/mm/fremap.c
index c395b1a..a1a2088 100644
--- a/mm/fremap.c
+++ b/mm/fremap.c
@@ -122,9 +122,14 @@ asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size,
 	struct address_space *mapping;
 	unsigned long end = start + size;
 	struct vm_area_struct *vma;
-	int err = -EINVAL;
+	int err;
 	int has_write_lock = 0;
 
+	err = update_current_cred();
+	if (err < 0)
+		return err;
+
+	err = -EINVAL;
 	if (__prot)
 		return err;
 	/*
diff --git a/mm/madvise.c b/mm/madvise.c
index 93ee375..508a584 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -286,10 +286,15 @@ asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
 	unsigned long end, tmp;
 	struct vm_area_struct * vma, *prev;
 	int unmapped_error = 0;
-	int error = -EINVAL;
+	int error;
 	int write;
 	size_t len;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
+	error = -EINVAL;
 	write = madvise_need_mmap_write(behavior);
 	if (write)
 		down_write(&current->mm->mmap_sem);
diff --git a/mm/msync.c b/mm/msync.c
index 144a757..f8c1b3b 100644
--- a/mm/msync.c
+++ b/mm/msync.c
@@ -34,8 +34,13 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
 	int unmapped_error = 0;
-	int error = -EINVAL;
+	int error;
 
+	error = update_current_cred();
+	if (error < 0)
+		return error;
+
+	error = -EINVAL;
 	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
 		goto out;
 	if (start & ~PAGE_MASK)
diff --git a/mm/shmem.c b/mm/shmem.c
index fcd19d3..40e835b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1394,8 +1394,6 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
 	inode = new_inode(sb);
 	if (inode) {
 		inode->i_mode = mode;
-		inode->i_uid = current->fsuid;
-		inode->i_gid = current->fsgid;
 		inode->i_blocks = 0;
 		inode->i_mapping->a_ops = &shmem_aops;
 		inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
@@ -2212,8 +2210,8 @@ static int shmem_fill_super(struct super_block *sb,
 	struct inode *inode;
 	struct dentry *root;
 	int mode   = S_IRWXUGO | S_ISVTX;
-	uid_t uid = current->fsuid;
-	gid_t gid = current->fsgid;
+	uid_t uid = current->cred->uid;
+	gid_t gid = current->cred->gid;
 	int err = -ENOMEM;
 	struct shmem_sb_info *sbinfo;
 	unsigned long blocks = 0;
diff --git a/net/socket.c b/net/socket.c
index 7d44453..50bfeef 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -483,8 +483,6 @@ static struct socket *sock_alloc(void)
 	sock = SOCKET_I(inode);
 
 	inode->i_mode = S_IFSOCK | S_IRWXUGO;
-	inode->i_uid = current->fsuid;
-	inode->i_gid = current->fsgid;
 
 	get_cpu_var(sockets_in_use)++;
 	put_cpu_var(sockets_in_use);
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 1ea2755..362a0de 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -267,7 +267,7 @@ rpcauth_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
  * Look up a process' credentials in the authentication cache
  */
 struct rpc_cred *
-rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
+rpcauth_lookup_credcache(struct rpc_auth *auth, struct cred *acred,
 		int flags)
 {
 	LIST_HEAD(free);
@@ -336,23 +336,13 @@ out:
 struct rpc_cred *
 rpcauth_lookupcred(struct rpc_auth *auth, int flags)
 {
-	struct auth_cred acred = {
-		.uid = current->fsuid,
-		.gid = current->fsgid,
-		.group_info = current->group_info,
-	};
-	struct rpc_cred *ret;
-
 	dprintk("RPC:       looking up %s cred\n",
 		auth->au_ops->au_name);
-	get_group_info(acred.group_info);
-	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
-	put_group_info(acred.group_info);
-	return ret;
+	return auth->au_ops->lookup_cred(auth, current->cred, flags);
 }
 
 void
-rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
+rpcauth_init_cred(struct rpc_cred *cred, const struct cred *acred,
 		  struct rpc_auth *auth, const struct rpc_credops *ops)
 {
 	INIT_HLIST_NODE(&cred->cr_hash);
@@ -372,25 +362,18 @@ struct rpc_cred *
 rpcauth_bindcred(struct rpc_task *task)
 {
 	struct rpc_auth *auth = task->tk_client->cl_auth;
-	struct auth_cred acred = {
-		.uid = current->fsuid,
-		.gid = current->fsgid,
-		.group_info = current->group_info,
-	};
 	struct rpc_cred *ret;
 	int flags = 0;
 
 	dprintk("RPC: %5u looking up %s cred\n",
 		task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
-	get_group_info(acred.group_info);
 	if (task->tk_flags & RPC_TASK_ROOTCREDS)
 		flags |= RPCAUTH_LOOKUP_ROOTCREDS;
-	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
+	ret = auth->au_ops->lookup_cred(auth, current->cred, flags);
 	if (!IS_ERR(ret))
 		task->tk_msg.rpc_cred = ret;
 	else
 		task->tk_status = PTR_ERR(ret);
-	put_group_info(acred.group_info);
 	return ret;
 }
 
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 53995af..bad2698 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -793,13 +793,13 @@ gss_destroy_cred(struct rpc_cred *cred)
  * Lookup RPCSEC_GSS cred for the current process
  */
 static struct rpc_cred *
-gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
+gss_lookup_cred(struct rpc_auth *auth, struct cred *acred, int flags)
 {
 	return rpcauth_lookup_credcache(auth, acred, flags);
 }
 
 static struct rpc_cred *
-gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
+gss_create_cred(struct rpc_auth *auth, struct cred *acred, int flags)
 {
 	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
 	struct gss_cred	*cred = NULL;
@@ -840,7 +840,7 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
 }
 
 static int
-gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
+gss_match(struct cred *acred, struct rpc_cred *rc, int flags)
 {
 	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
 
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c
index 537d0e8..c2fcefa 100644
--- a/net/sunrpc/auth_null.c
+++ b/net/sunrpc/auth_null.c
@@ -34,7 +34,7 @@ nul_destroy(struct rpc_auth *auth)
  * Lookup NULL creds for current process
  */
 static struct rpc_cred *
-nul_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
+nul_lookup_cred(struct rpc_auth *auth, struct cred *acred, int flags)
 {
 	return get_rpccred(&null_cred);
 }
@@ -51,7 +51,7 @@ nul_destroy_cred(struct rpc_cred *cred)
  * Match cred handle against current process
  */
 static int
-nul_match(struct auth_cred *acred, struct rpc_cred *cred, int taskflags)
+nul_match(struct cred *acred, struct rpc_cred *cred, int taskflags)
 {
 	return 1;
 }
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
index 5ed91e5..f5ab6d7 100644
--- a/net/sunrpc/auth_unix.c
+++ b/net/sunrpc/auth_unix.c
@@ -51,13 +51,13 @@ unx_destroy(struct rpc_auth *auth)
  * Lookup AUTH_UNIX creds for current process
  */
 static struct rpc_cred *
-unx_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
+unx_lookup_cred(struct rpc_auth *auth, struct cred *acred, int flags)
 {
 	return rpcauth_lookup_credcache(auth, acred, flags);
 }
 
 static struct rpc_cred *
-unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
+unx_create_cred(struct rpc_auth *auth, struct cred *acred, int flags)
 {
 	struct unx_cred	*cred;
 	int		i;
@@ -115,7 +115,7 @@ unx_destroy_cred(struct rpc_cred *cred)
  * request root creds (e.g. for NFS swapping).
  */
 static int
-unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags)
+unx_match(struct cred *acred, struct rpc_cred *rcred, int flags)
 {
 	struct unx_cred	*cred = container_of(rcred, struct unx_cred, uc_base);
 	int		i;
diff --git a/security/dummy.c b/security/dummy.c
index 853ec22..62de89c 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -43,10 +43,12 @@ static int dummy_capget (struct task_struct *target, kernel_cap_t * effective,
 			*permitted |= (~0 & ~CAP_FS_MASK);
 			*effective |= (~0 & ~CAP_TO_MASK(CAP_SETPCAP) & ~CAP_FS_MASK);
 		}
-		if (target->fsuid == 0) {
+		rcu_read_lock();
+		if (task_cred(target)->uid == 0) {
 			*permitted |= CAP_FS_MASK;
 			*effective |= CAP_FS_MASK;
 		}
+		rcu_read_unlock();
 	}
 	return 0;
 }
@@ -138,8 +140,11 @@ static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
 		}
 	}
 
-	current->suid = current->euid = current->fsuid = bprm->e_uid;
-	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+	current->suid = current->euid = bprm->e_uid;
+	current->sgid = current->egid = bprm->e_gid;
+
+	change_fsuid(bprm->cred, bprm->e_uid);
+	change_fsgid(bprm->cred, bprm->e_gid);
 
 	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
 }
@@ -572,7 +577,7 @@ static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3,
 
 static void dummy_task_reparent_to_init (struct task_struct *p)
 {
-	p->euid = p->fsuid = 0;
+	p->euid = 0;
 	return;
 }
 
diff --git a/security/keys/compat.c b/security/keys/compat.c
index e10ec99..710f87a 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -25,6 +25,12 @@
 asmlinkage long compat_sys_keyctl(u32 option,
 				  u32 arg2, u32 arg3, u32 arg4, u32 arg5)
 {
+	int ret;
+
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	switch (option) {
 	case KEYCTL_GET_KEYRING_ID:
 		return keyctl_get_keyring_ID(arg2, arg3);
diff --git a/security/keys/key.c b/security/keys/key.c
index 01bbc6d..a012e6c 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -817,7 +817,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 		perm |= KEY_USR_WRITE;
 
 	/* allocate a new key */
-	key = key_alloc(ktype, description, current->fsuid, current->fsgid,
+	key = key_alloc(ktype, description,
+			current->cred->uid, current->cred->gid,
 			current, perm, flags);
 	if (IS_ERR(key)) {
 		key_ref = ERR_PTR(PTR_ERR(key));
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index d9ca15c..2555c3b 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -67,6 +67,10 @@ asmlinkage long sys_add_key(const char __user *_type,
 	if (plen > 32767)
 		goto error;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		goto error;
+
 	/* draw all the data into kernel space */
 	ret = key_get_type_from_user(type, _type, sizeof(type));
 	if (ret < 0)
@@ -143,6 +147,10 @@ asmlinkage long sys_request_key(const char __user *_type,
 	char type[32], *description, *callout_info;
 	long ret;
 
+	ret = update_current_cred();
+	if (ret < 0)
+		goto error;
+
 	/* pull the type into kernel space */
 	ret = key_get_type_from_user(type, _type, sizeof(type));
 	if (ret < 0)
@@ -794,7 +802,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
 	down_write(&key->sem);
 
 	/* if we're not the sysadmin, we can only change a key that we own */
-	if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) {
+	if (capable(CAP_SYS_ADMIN) || key->uid == current->cred->uid) {
 		key->perm = perm;
 		ret = 0;
 	}
@@ -1062,6 +1070,12 @@ error:
 asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
 			   unsigned long arg4, unsigned long arg5)
 {
+	int ret;
+
+	ret = update_current_cred();
+	if (ret < 0)
+		return ret;
+
 	switch (option) {
 	case KEYCTL_GET_KEYRING_ID:
 		return keyctl_get_keyring_ID((key_serial_t) arg2,
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 3b41f9b..f0f0452 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -22,14 +22,19 @@ int key_task_permission(const key_ref_t key_ref,
 			struct task_struct *context,
 			key_perm_t perm)
 {
+	struct cred *cred;
 	struct key *key;
 	key_perm_t kperm;
 	int ret;
 
+	rcu_read_lock();
+	cred = get_task_cred(context);
+	rcu_read_unlock();
+
 	key = key_ref_to_ptr(key_ref);
 
 	/* use the second 8-bits of permissions for keys the caller owns */
-	if (key->uid == context->fsuid) {
+	if (key->uid == cred->uid) {
 		kperm = key->perm >> 16;
 		goto use_these_perms;
 	}
@@ -37,15 +42,12 @@ int key_task_permission(const key_ref_t key_ref,
 	/* use the third 8-bits of permissions for keys the caller has a group
 	 * membership in common with */
 	if (key->gid != -1 && key->perm & KEY_GRP_ALL) {
-		if (key->gid == context->fsgid) {
+		if (key->gid == cred->gid) {
 			kperm = key->perm >> 8;
 			goto use_these_perms;
 		}
 
-		task_lock(context);
-		ret = groups_search(context->group_info, key->gid);
-		task_unlock(context);
-
+		ret = groups_search(cred->group_info, key->gid);
 		if (ret) {
 			kperm = key->perm >> 8;
 			goto use_these_perms;
@@ -56,6 +58,8 @@ int key_task_permission(const key_ref_t key_ref,
 	kperm = key->perm;
 
 use_these_perms:
+	put_cred(cred);
+
 	/* use the top 8-bits of permissions for keys the caller possesses
 	 * - possessor permissions are additive with other permissions
 	 */
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index b6f8680..97c99d1 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -357,13 +357,14 @@ int suid_keys(struct task_struct *tsk)
 /*****************************************************************************/
 /*
  * the filesystem user ID changed
+ * - can only be used for current task
  */
 void key_fsuid_changed(struct task_struct *tsk)
 {
 	/* update the ownership of the thread keyring */
 	if (tsk->thread_keyring) {
 		down_write(&tsk->thread_keyring->sem);
-		tsk->thread_keyring->uid = tsk->fsuid;
+		tsk->thread_keyring->uid = tsk->cred->uid;
 		up_write(&tsk->thread_keyring->sem);
 	}
 
@@ -372,13 +373,14 @@ void key_fsuid_changed(struct task_struct *tsk)
 /*****************************************************************************/
 /*
  * the filesystem group ID changed
+ * - can only be used for current task
  */
 void key_fsgid_changed(struct task_struct *tsk)
 {
 	/* update the ownership of the thread keyring */
 	if (tsk->thread_keyring) {
 		down_write(&tsk->thread_keyring->sem);
-		tsk->thread_keyring->gid = tsk->fsgid;
+		tsk->thread_keyring->gid = tsk->cred->gid;
 		up_write(&tsk->thread_keyring->sem);
 	}
 
@@ -398,10 +400,13 @@ key_ref_t search_process_keyrings(struct key_type *type,
 				  struct task_struct *context)
 {
 	struct request_key_auth *rka;
+	struct cred *cred;
 	key_ref_t key_ref, ret, err;
 
 	might_sleep();
 
+	cred = get_current_cred();
+
 	/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
 	 * searchable, but we failed to find a key or we found a negative key;
 	 * otherwise we want to return a sample error (probably -EACCES) if
@@ -414,10 +419,9 @@ key_ref_t search_process_keyrings(struct key_type *type,
 	err = ERR_PTR(-EAGAIN);
 
 	/* search the thread keyring first */
-	if (context->thread_keyring) {
-		key_ref = keyring_search_aux(
-			make_key_ref(context->thread_keyring, 1),
-			context, type, description, match);
+	if (cred->thread_keyring) {
+		key_ref = keyring_search_aux(cred->thread_keyring, context,
+					     type, description, match);
 		if (!IS_ERR(key_ref))
 			goto found;
 
@@ -435,10 +439,9 @@ key_ref_t search_process_keyrings(struct key_type *type,
 	}
 
 	/* search the process keyring second */
-	if (context->signal->process_keyring) {
-		key_ref = keyring_search_aux(
-			make_key_ref(context->signal->process_keyring, 1),
-			context, type, description, match);
+	if (cred->process_keyring) {
+		key_ref = keyring_search_aux(cred->process_keyring, context,
+					     type, description, match);
 		if (!IS_ERR(key_ref))
 			goto found;
 
@@ -455,36 +458,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
 		}
 	}
 
-	/* search the session keyring */
-	if (context->signal->session_keyring) {
-		rcu_read_lock();
-		key_ref = keyring_search_aux(
-			make_key_ref(rcu_dereference(
-					     context->signal->session_keyring),
-				     1),
-			context, type, description, match);
-		rcu_read_unlock();
-
-		if (!IS_ERR(key_ref))
-			goto found;
-
-		switch (PTR_ERR(key_ref)) {
-		case -EAGAIN: /* no key */
-			if (ret)
-				break;
-		case -ENOKEY: /* negative key */
-			ret = key_ref;
-			break;
-		default:
-			err = key_ref;
-			break;
-		}
-	}
-	/* or search the user-session keyring */
-	else {
-		key_ref = keyring_search_aux(
-			make_key_ref(context->user->session_keyring, 1),
-			context, type, description, match);
+	/* search the session or user-session keyring */
+	if (cred->session_keyring) {
+		key_ref = keyring_search_aux(cred->session_keyring, context,
+					     type, description, match);
 		if (!IS_ERR(key_ref))
 			goto found;
 
@@ -543,6 +520,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
 	key_ref = ret ? ret : err;
 
 found:
+	put_cred(cred);
 	return key_ref;
 
 } /* end search_process_keyrings() */
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 5575001..cf3a9da 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -49,8 +49,8 @@ static int call_sbin_request_key(struct key *key,
 	/* allocate a new session keyring */
 	sprintf(desc, "_req.%u", key->serial);
 
-	keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current,
-				KEY_ALLOC_QUOTA_OVERRUN, NULL);
+	keyring = keyring_alloc(desc, current->cred->uid, current->cred->gid,
+				current, KEY_ALLOC_QUOTA_OVERRUN, NULL);
 	if (IS_ERR(keyring)) {
 		ret = PTR_ERR(keyring);
 		goto error_alloc;
@@ -62,8 +62,8 @@ static int call_sbin_request_key(struct key *key,
 		goto error_link;
 
 	/* record the UID and GID */
-	sprintf(uid_str, "%d", current->fsuid);
-	sprintf(gid_str, "%d", current->fsgid);
+	sprintf(uid_str, "%d", current->cred->uid);
+	sprintf(gid_str, "%d", current->cred->gid);
 
 	/* we say which key is under construction */
 	sprintf(key_str, "%d", key->serial);
@@ -142,8 +142,8 @@ static struct key *__request_key_construction(struct key_type *type,
 
 	/* create a key and add it to the queue */
 	key = key_alloc(type, description,
-			current->fsuid, current->fsgid, current, KEY_POS_ALL,
-			flags);
+			current->cred->uid, current->cred->gid, current,
+			KEY_POS_ALL, flags);
 	if (IS_ERR(key))
 		goto alloc_failed;
 
@@ -428,7 +428,7 @@ struct key *request_key_and_link(struct key_type *type,
 			goto error;
 
 		/* - get hold of the user's construction queue */
-		user = key_user_lookup(current->fsuid);
+		user = key_user_lookup(current->cred->uid);
 		if (!user)
 			goto nomem;
 
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index cbf58a9..8644182 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -185,7 +185,7 @@ struct key *request_key_auth_new(struct key *target, const char *callout_info)
 	sprintf(desc, "%x", target->serial);
 
 	authkey = key_alloc(&key_type_request_key_auth, desc,
-			    current->fsuid, current->fsgid, current,
+			    current->cred->uid, current->cred->gid, current,
 			    KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
 			    KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
 	if (IS_ERR(authkey)) {


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-19 16:17 [PATCH 0/3] Introduce credential record David Howells
  2007-09-19 16:17 ` [PATCH 1/3] CRED: Introduce a COW credentials record David Howells
@ 2007-09-19 16:18 ` David Howells
  2007-09-19 17:28   ` Casey Schaufler
  2007-09-24 14:00   ` Serge E. Hallyn
  2007-09-19 16:18 ` [PATCH 3/3] CRED: Move the effective capabilities into the cred struct David Howells
  2 siblings, 2 replies; 22+ messages in thread
From: David Howells @ 2007-09-19 16:18 UTC (permalink / raw)
  To: viro, hch, Trond.Myklebust, sds, casey
  Cc: linux-kernel, selinux, linux-security-module, dhowells

Move into the cred struct the part of the task security data that defines how a
task acts upon an object.  The part that defines how something acts upon a task
remains attached to the task.

For SELinux this requires some of task_security_struct to be split off into
cred_security_struct which is then attached to struct cred.  Note that the
contents of cred_security_struct may not be changed without the generation of a
new struct cred.

The split is as follows:

 (*) create_sid, keycreate_sid and sockcreate_sid just move across.

 (*) sid is split into victim_sid - which remains - and action_sid - which
     migrates.

 (*) osid, exec_sid and ptrace_sid remain.

victim_sid is the SID used to govern actions upon the task.  action_sid is used
to govern actions made by the task.

When accessing the cred_security_struct of another process, RCU read procedures
must be observed.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 include/linux/cred.h              |    1 
 include/linux/security.h          |   34 +++
 kernel/cred.c                     |    7 +
 security/dummy.c                  |   11 +
 security/selinux/exports.c        |    6 
 security/selinux/hooks.c          |  497 +++++++++++++++++++++++--------------
 security/selinux/include/objsec.h |   16 +
 security/selinux/selinuxfs.c      |    8 -
 security/selinux/xfrm.c           |    6 
 9 files changed, 380 insertions(+), 206 deletions(-)

diff --git a/include/linux/cred.h b/include/linux/cred.h
index 22ae610..6c6feec 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -26,6 +26,7 @@ struct cred {
 	gid_t			gid;		/* fsgid as was */
 	struct rcu_head		exterminate;	/* cred destroyer */
 	struct group_info	*group_info;
+	void			*security;
 
 	/* caches for references to the three task keyrings
 	 * - note that key_ref_t isn't typedef'd at this point, hence the odd
diff --git a/include/linux/security.h b/include/linux/security.h
index 1a15526..e5ed2ea 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -504,6 +504,18 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hooks for credential structure operations.
+ *
+ * @cred_dup:
+ *	Duplicate the credentials onto a duplicated cred structure.
+ *	@cred points to the credentials structure.  cred->security points to the
+ *	security struct that was attached to the original cred struct, but it
+ *	lacks a reference for the duplication if reference counting is needed.
+ *
+ * @cred_destroy:
+ *	Destroy the credentials attached to a cred structure.
+ *	@cred points to the credentials structure that is to be destroyed.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1257,6 +1269,9 @@ struct security_operations {
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
 
+	int (*cred_dup)(struct cred *cred);
+	void (*cred_destroy)(struct cred *cred);
+
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
 	void (*task_free_security) (struct task_struct * p);
@@ -1864,6 +1879,16 @@ static inline int security_file_receive (struct file *file)
 	return security_ops->file_receive (file);
 }
 
+static inline int security_cred_dup(struct cred *cred)
+{
+	return security_ops->cred_dup(cred);
+}
+
+static inline void security_cred_destroy(struct cred *cred)
+{
+	return security_ops->cred_destroy(cred);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);
@@ -2546,6 +2571,15 @@ static inline int security_file_receive (struct file *file)
 	return 0;
 }
 
+static inline int security_cred_dup(struct cred *cred)
+{
+	return 0;
+}
+
+static inline void security_cred_destroy(struct cred *cred)
+{
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return 0;
diff --git a/kernel/cred.c b/kernel/cred.c
index e96dafe..6a9dda2 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -92,6 +92,12 @@ struct cred *dup_cred(const struct cred *pcred)
 	if (likely(cred)) {
 		*cred = *pcred;
 		atomic_set(&cred->usage, 1);
+
+		if (security_cred_dup(cred) < 0) {
+			kfree(cred);
+			return NULL;
+		}
+
 		get_group_info(cred->group_info);
 		key_get(key_ref_to_ptr(cred->session_keyring));
 		key_get(key_ref_to_ptr(cred->process_keyring));
@@ -109,6 +115,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
 {
 	struct cred *cred = container_of(rcu, struct cred, exterminate);
 
+	security_cred_destroy(cred);
 	put_group_info(cred->group_info);
 	key_ref_put(cred->session_keyring);
 	key_ref_put(cred->process_keyring);
diff --git a/security/dummy.c b/security/dummy.c
index 62de89c..f535cc6 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -468,6 +468,15 @@ static int dummy_file_receive (struct file *file)
 	return 0;
 }
 
+static int dummy_cred_dup(struct cred *cred)
+{
+	return 0;
+}
+
+static void dummy_cred_destroy(struct cred *cred)
+{
+}
+
 static int dummy_task_create (unsigned long clone_flags)
 {
 	return 0;
@@ -1038,6 +1047,8 @@ void security_fixup_ops (struct security_operations *ops)
 	set_to_dummy_if_null(ops, file_set_fowner);
 	set_to_dummy_if_null(ops, file_send_sigiotask);
 	set_to_dummy_if_null(ops, file_receive);
+	set_to_dummy_if_null(ops, cred_dup);
+	set_to_dummy_if_null(ops, cred_destroy);
 	set_to_dummy_if_null(ops, task_create);
 	set_to_dummy_if_null(ops, task_alloc_security);
 	set_to_dummy_if_null(ops, task_free_security);
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
index b6f9694..29cb87a 100644
--- a/security/selinux/exports.c
+++ b/security/selinux/exports.c
@@ -57,7 +57,7 @@ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
 {
 	if (selinux_enabled) {
 		struct task_security_struct *tsec = tsk->security;
-		*sid = tsec->sid;
+		*sid = tsec->victim_sid;
 		return;
 	}
 	*sid = 0;
@@ -77,9 +77,9 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
 int selinux_relabel_packet_permission(u32 sid)
 {
 	if (selinux_enabled) {
-		struct task_security_struct *tsec = current->security;
+		struct cred_security_struct *csec = current->cred->security;
 
-		return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
+		return avc_has_perm(csec->action_sid, sid, SECCLASS_PACKET,
 				    PACKET__RELABELTO, NULL);
 	}
 	return 0;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3694662..6fc41da 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -162,7 +162,8 @@ static int task_alloc_security(struct task_struct *task)
 		return -ENOMEM;
 
 	tsec->task = task;
-	tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
+	tsec->osid = tsec->victim_sid = tsec->ptrace_sid =
+		SECINITSID_UNLABELED;
 	task->security = tsec;
 
 	return 0;
@@ -177,7 +178,7 @@ static void task_free_security(struct task_struct *task)
 
 static int inode_alloc_security(struct inode *inode)
 {
-	struct task_security_struct *tsec = current->security;
+	struct cred_security_struct *csec = current->cred->security;
 	struct inode_security_struct *isec;
 
 	isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL);
@@ -189,7 +190,7 @@ static int inode_alloc_security(struct inode *inode)
 	isec->inode = inode;
 	isec->sid = SECINITSID_UNLABELED;
 	isec->sclass = SECCLASS_FILE;
-	isec->task_sid = tsec->sid;
+	isec->task_sid = csec->action_sid;
 	inode->i_security = isec;
 
 	return 0;
@@ -211,7 +212,7 @@ static void inode_free_security(struct inode *inode)
 
 static int file_alloc_security(struct file *file)
 {
-	struct task_security_struct *tsec = current->security;
+	struct cred_security_struct *csec = current->cred->security;
 	struct file_security_struct *fsec;
 
 	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
@@ -219,8 +220,8 @@ static int file_alloc_security(struct file *file)
 		return -ENOMEM;
 
 	fsec->file = file;
-	fsec->sid = tsec->sid;
-	fsec->fown_sid = tsec->sid;
+	fsec->sid = csec->action_sid;
+	fsec->fown_sid = csec->action_sid;
 	file->f_security = fsec;
 
 	return 0;
@@ -333,26 +334,26 @@ static match_table_t tokens = {
 
 static int may_context_mount_sb_relabel(u32 sid,
 			struct superblock_security_struct *sbsec,
-			struct task_security_struct *tsec)
+			struct cred_security_struct *csec)
 {
 	int rc;
 
-	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
 			  FILESYSTEM__RELABELFROM, NULL);
 	if (rc)
 		return rc;
 
-	rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+	rc = avc_has_perm(csec->action_sid, sid, SECCLASS_FILESYSTEM,
 			  FILESYSTEM__RELABELTO, NULL);
 	return rc;
 }
 
 static int may_context_mount_inode_relabel(u32 sid,
 			struct superblock_security_struct *sbsec,
-			struct task_security_struct *tsec)
+			struct cred_security_struct *csec)
 {
 	int rc;
-	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
 			  FILESYSTEM__RELABELFROM, NULL);
 	if (rc)
 		return rc;
@@ -369,7 +370,7 @@ static int try_context_mount(struct super_block *sb, void *data)
 	const char *name;
 	u32 sid;
 	int alloc = 0, rc = 0, seen = 0;
-	struct task_security_struct *tsec = current->security;
+	struct cred_security_struct *csec = current->cred->security;
 	struct superblock_security_struct *sbsec = sb->s_security;
 
 	if (!data)
@@ -501,7 +502,7 @@ static int try_context_mount(struct super_block *sb, void *data)
 			goto out_free;
 		}
 
-		rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+		rc = may_context_mount_sb_relabel(sid, sbsec, csec);
 		if (rc)
 			goto out_free;
 
@@ -523,12 +524,12 @@ static int try_context_mount(struct super_block *sb, void *data)
 		}
 
 		if (!fscontext) {
-			rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+			rc = may_context_mount_sb_relabel(sid, sbsec, csec);
 			if (rc)
 				goto out_free;
 			sbsec->sid = sid;
 		} else {
-			rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+			rc = may_context_mount_inode_relabel(sid, sbsec, csec);
 			if (rc)
 				goto out_free;
 		}
@@ -548,7 +549,7 @@ static int try_context_mount(struct super_block *sb, void *data)
 			goto out_free;
 		}
 
-		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
 		if (rc)
 			goto out_free;
 
@@ -568,7 +569,7 @@ static int try_context_mount(struct super_block *sb, void *data)
 		if (sid == sbsec->def_sid)
 			goto out_free;
 
-		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
 		if (rc)
 			goto out_free;
 
@@ -1023,15 +1024,22 @@ static inline u32 signal_to_av(int sig)
 
 /* Check permission betweeen a pair of tasks, e.g. signal checks,
    fork check, ptrace check, etc. */
-static int task_has_perm(struct task_struct *tsk1,
-			 struct task_struct *tsk2,
+static int task_has_perm(struct task_struct *actor,
+			 struct task_struct *victim,
 			 u32 perms)
 {
-	struct task_security_struct *tsec1, *tsec2;
+	struct cred_security_struct *csec;
+	struct task_security_struct *tsec;
+	u32 action_sid;
+
+	/* the actor may not be the current task */
+	rcu_read_lock();
+	csec = task_cred(actor)->security;
+	action_sid = csec->action_sid;
+	rcu_read_unlock();
 
-	tsec1 = tsk1->security;
-	tsec2 = tsk2->security;
-	return avc_has_perm(tsec1->sid, tsec2->sid,
+	tsec = victim->security;
+	return avc_has_perm(action_sid, tsec->victim_sid,
 			    SECCLASS_PROCESS, perms, NULL);
 }
 
@@ -1039,16 +1047,16 @@ static int task_has_perm(struct task_struct *tsk1,
 static int task_has_capability(struct task_struct *tsk,
 			       int cap)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct avc_audit_data ad;
 
-	tsec = tsk->security;
+	csec = tsk->cred->security;
 
 	AVC_AUDIT_DATA_INIT(&ad,CAP);
 	ad.tsk = tsk;
 	ad.u.cap = cap;
 
-	return avc_has_perm(tsec->sid, tsec->sid,
+	return avc_has_perm(csec->action_sid, csec->action_sid,
 			    SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
 }
 
@@ -1056,11 +1064,11 @@ static int task_has_capability(struct task_struct *tsk,
 static int task_has_system(struct task_struct *tsk,
 			   u32 perms)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 
-	tsec = tsk->security;
+	csec = tsk->cred->security;
 
-	return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+	return avc_has_perm(csec->action_sid, SECINITSID_KERNEL,
 			    SECCLASS_SYSTEM, perms, NULL);
 }
 
@@ -1072,14 +1080,14 @@ static int inode_has_perm(struct task_struct *tsk,
 			  u32 perms,
 			  struct avc_audit_data *adp)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct inode_security_struct *isec;
 	struct avc_audit_data ad;
 
 	if (unlikely (IS_PRIVATE (inode)))
 		return 0;
 
-	tsec = tsk->security;
+	csec = tsk->cred->security;
 	isec = inode->i_security;
 
 	if (!adp) {
@@ -1088,7 +1096,8 @@ static int inode_has_perm(struct task_struct *tsk,
 		ad.u.fs.inode = inode;
 	}
 
-	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
+			    adp);
 }
 
 /* Same as inode_has_perm, but pass explicit audit data containing
@@ -1119,7 +1128,7 @@ static int file_has_perm(struct task_struct *tsk,
 				struct file *file,
 				u32 av)
 {
-	struct task_security_struct *tsec = tsk->security;
+	struct cred_security_struct *csec = tsk->cred->security;
 	struct file_security_struct *fsec = file->f_security;
 	struct vfsmount *mnt = file->f_path.mnt;
 	struct dentry *dentry = file->f_path.dentry;
@@ -1131,8 +1140,8 @@ static int file_has_perm(struct task_struct *tsk,
 	ad.u.fs.mnt = mnt;
 	ad.u.fs.dentry = dentry;
 
-	if (tsec->sid != fsec->sid) {
-		rc = avc_has_perm(tsec->sid, fsec->sid,
+	if (csec->action_sid != fsec->sid) {
+		rc = avc_has_perm(csec->action_sid, fsec->sid,
 				  SECCLASS_FD,
 				  FD__USE,
 				  &ad);
@@ -1152,36 +1161,36 @@ static int may_create(struct inode *dir,
 		      struct dentry *dentry,
 		      u16 tclass)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct inode_security_struct *dsec;
 	struct superblock_security_struct *sbsec;
 	u32 newsid;
 	struct avc_audit_data ad;
 	int rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	dsec = dir->i_security;
 	sbsec = dir->i_sb->s_security;
 
 	AVC_AUDIT_DATA_INIT(&ad, FS);
 	ad.u.fs.dentry = dentry;
 
-	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR,
 			  DIR__ADD_NAME | DIR__SEARCH,
 			  &ad);
 	if (rc)
 		return rc;
 
-	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
-		newsid = tsec->create_sid;
+	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+		newsid = csec->create_sid;
 	} else {
-		rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
-					     &newsid);
+		rc = security_transition_sid(csec->action_sid, dsec->sid,
+					     tclass, &newsid);
 		if (rc)
 			return rc;
 	}
 
-	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+	rc = avc_has_perm(csec->action_sid, newsid, tclass, FILE__CREATE, &ad);
 	if (rc)
 		return rc;
 
@@ -1194,11 +1203,12 @@ static int may_create(struct inode *dir,
 static int may_create_key(u32 ksid,
 			  struct task_struct *ctx)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 
-	tsec = ctx->security;
+	csec = ctx->cred->security;
 
-	return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+	return avc_has_perm(csec->action_sid, ksid, SECCLASS_KEY, KEY__CREATE,
+			    NULL);
 }
 
 #define MAY_LINK   0
@@ -1211,13 +1221,13 @@ static int may_link(struct inode *dir,
 		    int kind)
 
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct inode_security_struct *dsec, *isec;
 	struct avc_audit_data ad;
 	u32 av;
 	int rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	dsec = dir->i_security;
 	isec = dentry->d_inode->i_security;
 
@@ -1226,7 +1236,7 @@ static int may_link(struct inode *dir,
 
 	av = DIR__SEARCH;
 	av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
-	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR, av, &ad);
 	if (rc)
 		return rc;
 
@@ -1245,7 +1255,7 @@ static int may_link(struct inode *dir,
 		return 0;
 	}
 
-	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, av, &ad);
 	return rc;
 }
 
@@ -1254,14 +1264,14 @@ static inline int may_rename(struct inode *old_dir,
 			     struct inode *new_dir,
 			     struct dentry *new_dentry)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
 	struct avc_audit_data ad;
 	u32 av;
 	int old_is_dir, new_is_dir;
 	int rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	old_dsec = old_dir->i_security;
 	old_isec = old_dentry->d_inode->i_security;
 	old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -1270,16 +1280,16 @@ static inline int may_rename(struct inode *old_dir,
 	AVC_AUDIT_DATA_INIT(&ad, FS);
 
 	ad.u.fs.dentry = old_dentry;
-	rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+	rc = avc_has_perm(csec->action_sid, old_dsec->sid, SECCLASS_DIR,
 			  DIR__REMOVE_NAME | DIR__SEARCH, &ad);
 	if (rc)
 		return rc;
-	rc = avc_has_perm(tsec->sid, old_isec->sid,
+	rc = avc_has_perm(csec->action_sid, old_isec->sid,
 			  old_isec->sclass, FILE__RENAME, &ad);
 	if (rc)
 		return rc;
 	if (old_is_dir && new_dir != old_dir) {
-		rc = avc_has_perm(tsec->sid, old_isec->sid,
+		rc = avc_has_perm(csec->action_sid, old_isec->sid,
 				  old_isec->sclass, DIR__REPARENT, &ad);
 		if (rc)
 			return rc;
@@ -1289,15 +1299,17 @@ static inline int may_rename(struct inode *old_dir,
 	av = DIR__ADD_NAME | DIR__SEARCH;
 	if (new_dentry->d_inode)
 		av |= DIR__REMOVE_NAME;
-	rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+	rc = avc_has_perm(csec->action_sid, new_dsec->sid, SECCLASS_DIR, av,
+			  &ad);
 	if (rc)
 		return rc;
 	if (new_dentry->d_inode) {
 		new_isec = new_dentry->d_inode->i_security;
 		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
-		rc = avc_has_perm(tsec->sid, new_isec->sid,
+		rc = avc_has_perm(csec->action_sid, new_isec->sid,
 				  new_isec->sclass,
-				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
+				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
+				  &ad);
 		if (rc)
 			return rc;
 	}
@@ -1311,12 +1323,12 @@ static int superblock_has_perm(struct task_struct *tsk,
 			       u32 perms,
 			       struct avc_audit_data *ad)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct superblock_security_struct *sbsec;
 
-	tsec = tsk->security;
+	csec = tsk->cred->security;
 	sbsec = sb->s_security;
-	return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+	return avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
 			    perms, ad);
 }
 
@@ -1369,7 +1381,7 @@ static inline u32 file_to_av(struct file *file)
 
 static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
 {
-	struct task_security_struct *psec = parent->security;
+	struct cred_security_struct *psec;
 	struct task_security_struct *csec = child->security;
 	int rc;
 
@@ -1379,8 +1391,12 @@ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
 
 	rc = task_has_perm(parent, child, PROCESS__PTRACE);
 	/* Save the SID of the tracing process for later use in apply_creds. */
-	if (!(child->ptrace & PT_PTRACED) && !rc)
-		csec->ptrace_sid = psec->sid;
+	if (!(child->ptrace & PT_PTRACED) && !rc) {
+		rcu_read_lock();
+		psec = task_cred(parent)->security;
+		csec->ptrace_sid = psec->action_sid;
+		rcu_read_unlock();
+	}
 	return rc;
 }
 
@@ -1470,7 +1486,7 @@ static int selinux_sysctl(ctl_table *table, int op)
 {
 	int error = 0;
 	u32 av;
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	u32 tsid;
 	int rc;
 
@@ -1478,7 +1494,7 @@ static int selinux_sysctl(ctl_table *table, int op)
 	if (rc)
 		return rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 
 	rc = selinux_sysctl_get_sid(table, (op == 0001) ?
 				    SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1490,7 +1506,7 @@ static int selinux_sysctl(ctl_table *table, int op)
 	/* The op values are "defined" in sysctl.c, thereby creating
 	 * a bad coupling between this module and sysctl.c */
 	if(op == 001) {
-		error = avc_has_perm(tsec->sid, tsid,
+		error = avc_has_perm(csec->action_sid, tsid,
 				     SECCLASS_DIR, DIR__SEARCH, NULL);
 	} else {
 		av = 0;
@@ -1499,7 +1515,7 @@ static int selinux_sysctl(ctl_table *table, int op)
 		if (op & 002)
 			av |= FILE__WRITE;
 		if (av)
-			error = avc_has_perm(tsec->sid, tsid,
+			error = avc_has_perm(csec->action_sid, tsid,
 					     SECCLASS_FILE, av, NULL);
         }
 
@@ -1587,11 +1603,11 @@ static int selinux_syslog(int type)
 static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 {
 	int rc, cap_sys_admin = 0;
-	struct task_security_struct *tsec = current->security;
+	struct cred_security_struct *csec = current->cred->security;
 
 	rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
 	if (rc == 0)
-		rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+		rc = avc_has_perm_noaudit(csec->action_sid, csec->action_sid,
 					  SECCLASS_CAPABILITY,
 					  CAP_TO_MASK(CAP_SYS_ADMIN),
 					  0,
@@ -1624,6 +1640,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
 static int selinux_bprm_set_security(struct linux_binprm *bprm)
 {
 	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct inode *inode = bprm->file->f_path.dentry->d_inode;
 	struct inode_security_struct *isec;
 	struct bprm_security_struct *bsec;
@@ -1641,15 +1658,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
 		return 0;
 
 	tsec = current->security;
+	csec = bprm->cred->security;
 	isec = inode->i_security;
 
 	/* Default to the current task SID. */
-	bsec->sid = tsec->sid;
+	bsec->sid = csec->action_sid;
 
 	/* Reset fs, key, and sock SIDs on execve. */
-	tsec->create_sid = 0;
-	tsec->keycreate_sid = 0;
-	tsec->sockcreate_sid = 0;
+	csec->create_sid = 0;
+	csec->keycreate_sid = 0;
+	csec->sockcreate_sid = 0;
 
 	if (tsec->exec_sid) {
 		newsid = tsec->exec_sid;
@@ -1657,7 +1675,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
 		tsec->exec_sid = 0;
 	} else {
 		/* Check for a default transition on this program. */
-		rc = security_transition_sid(tsec->sid, isec->sid,
+		rc = security_transition_sid(csec->action_sid, isec->sid,
 		                             SECCLASS_PROCESS, &newsid);
 		if (rc)
 			return rc;
@@ -1668,16 +1686,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
 	ad.u.fs.dentry = bprm->file->f_path.dentry;
 
 	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
-		newsid = tsec->sid;
+		newsid = csec->action_sid;
 
-        if (tsec->sid == newsid) {
-		rc = avc_has_perm(tsec->sid, isec->sid,
+        if (csec->action_sid == newsid) {
+		rc = avc_has_perm(csec->action_sid, isec->sid,
 				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
 		if (rc)
 			return rc;
 	} else {
 		/* Check permissions for the transition. */
-		rc = avc_has_perm(tsec->sid, newsid,
+		rc = avc_has_perm(csec->action_sid, newsid,
 				  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
 		if (rc)
 			return rc;
@@ -1709,11 +1727,11 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm)
 	struct task_security_struct *tsec = current->security;
 	int atsecure = 0;
 
-	if (tsec->osid != tsec->sid) {
+	if (tsec->osid != tsec->victim_sid) {
 		/* Enable secure mode for SIDs transitions unless
 		   the noatsecure permission is granted between
 		   the two SIDs, i.e. ahp returns 0. */
-		atsecure = avc_has_perm(tsec->osid, tsec->sid,
+		atsecure = avc_has_perm(tsec->osid, tsec->victim_sid,
 					 SECCLASS_PROCESS,
 					 PROCESS__NOATSECURE, NULL);
 	}
@@ -1823,6 +1841,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
 static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
 {
 	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct bprm_security_struct *bsec;
 	u32 sid;
 	int rc;
@@ -1830,17 +1849,17 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
 	secondary_ops->bprm_apply_creds(bprm, unsafe);
 
 	tsec = current->security;
-
+	csec = bprm->cred->security;
 	bsec = bprm->security;
 	sid = bsec->sid;
 
-	tsec->osid = tsec->sid;
+	tsec->osid = tsec->victim_sid;
 	bsec->unsafe = 0;
-	if (tsec->sid != sid) {
+	if (tsec->victim_sid != sid) {
 		/* Check for shared state.  If not ok, leave SID
 		   unchanged and kill. */
 		if (unsafe & LSM_UNSAFE_SHARE) {
-			rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+			rc = avc_has_perm(tsec->victim_sid, sid, SECCLASS_PROCESS,
 					PROCESS__SHARE, NULL);
 			if (rc) {
 				bsec->unsafe = 1;
@@ -1859,7 +1878,9 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
 				return;
 			}
 		}
-		tsec->sid = sid;
+		if (csec->action_sid == tsec->victim_sid)
+			csec->action_sid = sid;
+		tsec->victim_sid = sid;
 	}
 }
 
@@ -1881,7 +1902,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
 		force_sig_specific(SIGKILL, current);
 		return;
 	}
-	if (tsec->osid == tsec->sid)
+	if (tsec->osid == tsec->victim_sid)
 		return;
 
 	/* Close files for which the new task SID is not authorized. */
@@ -1893,7 +1914,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
 	   signals. This must occur _after_ the task SID has
 	  been updated so that any kill done after the flush
 	  will be checked against the new SID. */
-	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
 			  PROCESS__SIGINH, NULL);
 	if (rc) {
 		memset(&itimer, 0, sizeof itimer);
@@ -1920,7 +1941,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
 	   than the default soft limit for cases where the default
 	   is lower than the hard limit, e.g. RLIMIT_CORE or
 	   RLIMIT_STACK.*/
-	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
 			  PROCESS__RLIMITINH, NULL);
 	if (rc) {
 		for (i = 0; i < RLIM_NLIMITS; i++) {
@@ -2122,21 +2143,21 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
 				       char **name, void **value,
 				       size_t *len)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct inode_security_struct *dsec;
 	struct superblock_security_struct *sbsec;
 	u32 newsid, clen;
 	int rc;
 	char *namep = NULL, *context;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	dsec = dir->i_security;
 	sbsec = dir->i_sb->s_security;
 
-	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
-		newsid = tsec->create_sid;
+	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+		newsid = csec->create_sid;
 	} else {
-		rc = security_transition_sid(tsec->sid, dsec->sid,
+		rc = security_transition_sid(csec->action_sid, dsec->sid,
 					     inode_mode_to_security_class(inode->i_mode),
 					     &newsid);
 		if (rc) {
@@ -2295,7 +2316,7 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 
 static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
 {
-	struct task_security_struct *tsec = current->security;
+	struct cred_security_struct *csec = current->cred->security;
 	struct inode *inode = dentry->d_inode;
 	struct inode_security_struct *isec = inode->i_security;
 	struct superblock_security_struct *sbsec;
@@ -2327,7 +2348,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
 	AVC_AUDIT_DATA_INIT(&ad,FS);
 	ad.u.fs.dentry = dentry;
 
-	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass,
 			  FILE__RELABELFROM, &ad);
 	if (rc)
 		return rc;
@@ -2336,12 +2357,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
 	if (rc)
 		return rc;
 
-	rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+	rc = avc_has_perm(csec->action_sid, newsid, isec->sclass,
 			  FILE__RELABELTO, &ad);
 	if (rc)
 		return rc;
 
-	rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+	rc = security_validate_transition(isec->sid, newsid, csec->action_sid,
 	                                  isec->sclass);
 	if (rc)
 		return rc;
@@ -2575,8 +2596,9 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
 			     unsigned long prot, unsigned long flags,
 			     unsigned long addr, unsigned long addr_only)
 {
+	struct cred_security_struct *csec = current->cred->security;
 	int rc = 0;
-	u32 sid = ((struct task_security_struct*)(current->security))->sid;
+	u32 sid = csec->action_sid;
 
 	if (addr < mmap_min_addr)
 		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
@@ -2690,7 +2712,7 @@ static int selinux_file_set_fowner(struct file *file)
 
 	tsec = current->security;
 	fsec = file->f_security;
-	fsec->fown_sid = tsec->sid;
+	fsec->fown_sid = tsec->victim_sid;
 
 	return 0;
 }
@@ -2714,7 +2736,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
 	else
 		perm = signal_to_av(signum);
 
-	return avc_has_perm(fsec->fown_sid, tsec->sid,
+	return avc_has_perm(fsec->fown_sid, tsec->victim_sid,
 			    SECCLASS_PROCESS, perm, NULL);
 }
 
@@ -2723,6 +2745,31 @@ static int selinux_file_receive(struct file *file)
 	return file_has_perm(current, file, file_to_av(file));
 }
 
+/* credential security operations */
+
+/*
+ * duplicate the security information attached to a credentials record that is
+ * itself undergoing duplication
+ */
+static int selinux_cred_dup(struct cred *cred)
+{
+	cred->security = kmemdup(cred->security,
+				 sizeof(struct cred_security_struct),
+				 GFP_KERNEL);
+	return cred->security ? 0 : -ENOMEM;
+}
+
+/*
+ * destroy the security information attached to a credentials record
+ * - this is done under RCU, and may not be associated with the task that set it
+ *   up
+ */
+static void selinux_cred_destroy(struct cred *cred)
+{
+	kfree(cred->security);
+}
+
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)
@@ -2749,13 +2796,10 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
 	tsec2 = tsk->security;
 
 	tsec2->osid = tsec1->osid;
-	tsec2->sid = tsec1->sid;
+	tsec2->victim_sid = tsec1->victim_sid;
 
-	/* Retain the exec, fs, key, and sock SIDs across fork */
+	/* Retain the exec SID across fork */
 	tsec2->exec_sid = tsec1->exec_sid;
-	tsec2->create_sid = tsec1->create_sid;
-	tsec2->keycreate_sid = tsec1->keycreate_sid;
-	tsec2->sockcreate_sid = tsec1->sockcreate_sid;
 
 	/* Retain ptracer SID across fork, if any.
 	   This will be reset by the ptrace hook upon any
@@ -2893,7 +2937,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
 		perm = signal_to_av(sig);
 	tsec = p->security;
 	if (secid)
-		rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+		rc = avc_has_perm(secid, tsec->victim_sid,
+				  SECCLASS_PROCESS, perm, NULL);
 	else
 		rc = task_has_perm(current, p, perm);
 	return rc;
@@ -2927,8 +2972,8 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
 	secondary_ops->task_reparent_to_init(p);
 
 	tsec = p->security;
-	tsec->osid = tsec->sid;
-	tsec->sid = SECINITSID_KERNEL;
+	tsec->osid = tsec->victim_sid;
+	tsec->victim_sid = SECINITSID_KERNEL;
 	return;
 }
 
@@ -2938,7 +2983,7 @@ static void selinux_task_to_inode(struct task_struct *p,
 	struct task_security_struct *tsec = p->security;
 	struct inode_security_struct *isec = inode->i_security;
 
-	isec->sid = tsec->sid;
+	isec->sid = tsec->victim_sid;
 	isec->initialized = 1;
 	return;
 }
@@ -3163,11 +3208,11 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
 			   u32 perms)
 {
 	struct inode_security_struct *isec;
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct avc_audit_data ad;
 	int err = 0;
 
-	tsec = task->security;
+	csec = task->cred->security;
 	isec = SOCK_INODE(sock)->i_security;
 
 	if (isec->sid == SECINITSID_KERNEL)
@@ -3175,7 +3220,8 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
 
 	AVC_AUDIT_DATA_INIT(&ad,NET);
 	ad.u.net.sk = sock->sk;
-	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+	err = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
+			   &ad);
 
 out:
 	return err;
@@ -3185,15 +3231,15 @@ static int selinux_socket_create(int family, int type,
 				 int protocol, int kern)
 {
 	int err = 0;
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	u32 newsid;
 
 	if (kern)
 		goto out;
 
-	tsec = current->security;
-	newsid = tsec->sockcreate_sid ? : tsec->sid;
-	err = avc_has_perm(tsec->sid, newsid,
+	csec = current->cred->security;
+	newsid = csec->sockcreate_sid ? : csec->action_sid;
+	err = avc_has_perm(csec->action_sid, newsid,
 			   socket_type_to_security_class(family, type,
 			   protocol), SOCKET__CREATE, NULL);
 
@@ -3206,14 +3252,14 @@ static int selinux_socket_post_create(struct socket *sock, int family,
 {
 	int err = 0;
 	struct inode_security_struct *isec;
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct sk_security_struct *sksec;
 	u32 newsid;
 
 	isec = SOCK_INODE(sock)->i_security;
 
-	tsec = current->security;
-	newsid = tsec->sockcreate_sid ? : tsec->sid;
+	csec = current->cred->security;
+	newsid = csec->sockcreate_sid ? : csec->action_sid;
 	isec->sclass = socket_type_to_security_class(family, type, protocol);
 	isec->sid = kern ? SECINITSID_KERNEL : newsid;
 	isec->initialized = 1;
@@ -4027,7 +4073,7 @@ static int ipc_alloc_security(struct task_struct *task,
 			      struct kern_ipc_perm *perm,
 			      u16 sclass)
 {
-	struct task_security_struct *tsec = task->security;
+	struct cred_security_struct *csec = task->cred->security;
 	struct ipc_security_struct *isec;
 
 	isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
@@ -4036,7 +4082,7 @@ static int ipc_alloc_security(struct task_struct *task,
 
 	isec->sclass = sclass;
 	isec->ipc_perm = perm;
-	isec->sid = tsec->sid;
+	isec->sid = csec->action_sid;
 	perm->security = isec;
 
 	return 0;
@@ -4075,17 +4121,18 @@ static void msg_msg_free_security(struct msg_msg *msg)
 static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
 			u32 perms)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = ipc_perms->security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
 	ad.u.ipc_id = ipc_perms->key;
 
-	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
+			    &ad);
 }
 
 static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -4101,7 +4148,7 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
 /* message queue security operations */
 static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 	int rc;
@@ -4110,13 +4157,13 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
 	if (rc)
 		return rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = msq->q_perm.security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
  	ad.u.ipc_id = msq->q_perm.key;
 
-	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
 			  MSGQ__CREATE, &ad);
 	if (rc) {
 		ipc_free_security(&msq->q_perm);
@@ -4132,17 +4179,17 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
 
 static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = msq->q_perm.security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
 	ad.u.ipc_id = msq->q_perm.key;
 
-	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
 			    MSGQ__ASSOCIATE, &ad);
 }
 
@@ -4176,13 +4223,13 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
 
 static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct msg_security_struct *msec;
 	struct avc_audit_data ad;
 	int rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = msq->q_perm.security;
 	msec = msg->security;
 
@@ -4194,7 +4241,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
 		 * Compute new sid based on current process and
 		 * message queue this message will be stored in
 		 */
-		rc = security_transition_sid(tsec->sid,
+		rc = security_transition_sid(csec->action_sid,
 					     isec->sid,
 					     SECCLASS_MSG,
 					     &msec->sid);
@@ -4206,11 +4253,11 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
 	ad.u.ipc_id = msq->q_perm.key;
 
 	/* Can this process write to the queue? */
-	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
 			  MSGQ__WRITE, &ad);
 	if (!rc)
 		/* Can this process send the message */
-		rc = avc_has_perm(tsec->sid, msec->sid,
+		rc = avc_has_perm(csec->action_sid, msec->sid,
 				  SECCLASS_MSG, MSG__SEND, &ad);
 	if (!rc)
 		/* Can the message be put in the queue? */
@@ -4237,10 +4284,10 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
  	ad.u.ipc_id = msq->q_perm.key;
 
-	rc = avc_has_perm(tsec->sid, isec->sid,
+	rc = avc_has_perm(tsec->victim_sid, isec->sid,
 			  SECCLASS_MSGQ, MSGQ__READ, &ad);
 	if (!rc)
-		rc = avc_has_perm(tsec->sid, msec->sid,
+		rc = avc_has_perm(tsec->victim_sid, msec->sid,
 				  SECCLASS_MSG, MSG__RECEIVE, &ad);
 	return rc;
 }
@@ -4248,7 +4295,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
 /* Shared Memory security operations */
 static int selinux_shm_alloc_security(struct shmid_kernel *shp)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 	int rc;
@@ -4257,13 +4304,13 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
 	if (rc)
 		return rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = shp->shm_perm.security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
  	ad.u.ipc_id = shp->shm_perm.key;
 
-	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
 			  SHM__CREATE, &ad);
 	if (rc) {
 		ipc_free_security(&shp->shm_perm);
@@ -4279,17 +4326,17 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
 
 static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = shp->shm_perm.security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
 	ad.u.ipc_id = shp->shm_perm.key;
 
-	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
 			    SHM__ASSOCIATE, &ad);
 }
 
@@ -4347,7 +4394,7 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
 /* Semaphore security operations */
 static int selinux_sem_alloc_security(struct sem_array *sma)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 	int rc;
@@ -4356,13 +4403,13 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
 	if (rc)
 		return rc;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = sma->sem_perm.security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
  	ad.u.ipc_id = sma->sem_perm.key;
 
-	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
 			  SEM__CREATE, &ad);
 	if (rc) {
 		ipc_free_security(&sma->sem_perm);
@@ -4378,17 +4425,17 @@ static void selinux_sem_free_security(struct sem_array *sma)
 
 static int selinux_sem_associate(struct sem_array *sma, int semflg)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct ipc_security_struct *isec;
 	struct avc_audit_data ad;
 
-	tsec = current->security;
+	csec = current->cred->security;
 	isec = sma->sem_perm.security;
 
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
 	ad.u.ipc_id = sma->sem_perm.key;
 
-	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
 			    SEM__ASSOCIATE, &ad);
 }
 
@@ -4504,6 +4551,7 @@ static int selinux_getprocattr(struct task_struct *p,
 			       char *name, char **value)
 {
 	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	u32 sid;
 	int error;
 	unsigned len;
@@ -4515,22 +4563,25 @@ static int selinux_getprocattr(struct task_struct *p,
 	}
 
 	tsec = p->security;
+	rcu_read_lock();
+	csec = task_cred(p)->security;
 
 	if (!strcmp(name, "current"))
-		sid = tsec->sid;
+		sid = tsec->victim_sid;
 	else if (!strcmp(name, "prev"))
 		sid = tsec->osid;
 	else if (!strcmp(name, "exec"))
 		sid = tsec->exec_sid;
 	else if (!strcmp(name, "fscreate"))
-		sid = tsec->create_sid;
+		sid = csec->create_sid;
 	else if (!strcmp(name, "keycreate"))
-		sid = tsec->keycreate_sid;
+		sid = csec->keycreate_sid;
 	else if (!strcmp(name, "sockcreate"))
-		sid = tsec->sockcreate_sid;
+		sid = csec->sockcreate_sid;
 	else
-		return -EINVAL;
+		goto invalid;
 
+	rcu_read_unlock();
 	if (!sid)
 		return 0;
 
@@ -4538,13 +4589,20 @@ static int selinux_getprocattr(struct task_struct *p,
 	if (error)
 		return error;
 	return len;
+
+invalid:
+	rcu_read_unlock();
+	return -EINVAL;
 }
 
 static int selinux_setprocattr(struct task_struct *p,
 			       char *name, void *value, size_t size)
 {
 	struct task_security_struct *tsec;
-	u32 sid = 0;
+	struct cred_security_struct *csec;
+	struct av_decision avd;
+	struct cred *cred;
+	u32 sid = 0, perm;
 	int error;
 	char *str = value;
 
@@ -4560,17 +4618,19 @@ static int selinux_setprocattr(struct task_struct *p,
 	 * above restriction is ever removed.
 	 */
 	if (!strcmp(name, "exec"))
-		error = task_has_perm(current, p, PROCESS__SETEXEC);
+		perm = PROCESS__SETEXEC;
 	else if (!strcmp(name, "fscreate"))
-		error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+		perm = PROCESS__SETFSCREATE;
 	else if (!strcmp(name, "keycreate"))
-		error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+		perm = PROCESS__SETKEYCREATE;
 	else if (!strcmp(name, "sockcreate"))
-		error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+		perm = PROCESS__SETSOCKCREATE;
 	else if (!strcmp(name, "current"))
-		error = task_has_perm(current, p, PROCESS__SETCURRENT);
+		perm = PROCESS__SETCURRENT;
 	else
-		error = -EINVAL;
+		return -EINVAL;
+
+	error = task_has_perm(current, p, perm);
 	if (error)
 		return error;
 
@@ -4592,20 +4652,37 @@ static int selinux_setprocattr(struct task_struct *p,
 	   checks and may_create for the file creation checks. The
 	   operation will then fail if the context is not permitted. */
 	tsec = p->security;
-	if (!strcmp(name, "exec"))
+	csec = p->cred->security;
+	switch (perm) {
+	case PROCESS__SETEXEC:
 		tsec->exec_sid = sid;
-	else if (!strcmp(name, "fscreate"))
-		tsec->create_sid = sid;
-	else if (!strcmp(name, "keycreate")) {
+		break;
+
+	case PROCESS__SETKEYCREATE:
 		error = may_create_key(sid, p);
 		if (error)
 			return error;
-		tsec->keycreate_sid = sid;
-	} else if (!strcmp(name, "sockcreate"))
-		tsec->sockcreate_sid = sid;
-	else if (!strcmp(name, "current")) {
-		struct av_decision avd;
+	case PROCESS__SETFSCREATE:
+	case PROCESS__SETSOCKCREATE:
+		cred = dup_cred(current->cred);
+		if (!cred)
+			return -ENOMEM;
+		csec = cred->security;
+		switch (perm) {
+		case PROCESS__SETKEYCREATE:
+			csec->keycreate_sid = sid;
+			break;
+		case PROCESS__SETFSCREATE:
+			csec->create_sid = sid;
+			break;
+		case PROCESS__SETSOCKCREATE:
+			csec->sockcreate_sid = sid;
+			break;
+		}
+		set_current_cred(cred);
+		break;
 
+	case PROCESS__SETCURRENT:
 		if (sid == 0)
 			return -EINVAL;
 
@@ -4624,11 +4701,16 @@ static int selinux_setprocattr(struct task_struct *p,
                 }
 
 		/* Check permissions for the transition. */
-		error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+		error = avc_has_perm(csec->action_sid, sid, SECCLASS_PROCESS,
 		                     PROCESS__DYNTRANSITION, NULL);
 		if (error)
 			return error;
 
+		cred = dup_cred(current->cred);
+		if (!cred)
+			return -ENOMEM;
+		csec = cred->security;
+
 		/* Check for ptracing, and update the task SID if ok.
 		   Otherwise, leave SID unchanged and fail. */
 		task_lock(p);
@@ -4636,20 +4718,25 @@ static int selinux_setprocattr(struct task_struct *p,
 			error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
 						     SECCLASS_PROCESS,
 						     PROCESS__PTRACE, 0, &avd);
-			if (!error)
-				tsec->sid = sid;
+			if (!error) {
+				csec->action_sid = tsec->victim_sid = sid;
+			}
 			task_unlock(p);
 			avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
 				  PROCESS__PTRACE, &avd, error, NULL);
-			if (error)
+			if (error) {
+				put_cred(cred);
 				return error;
+			}
 		} else {
-			tsec->sid = sid;
+			csec->action_sid = tsec->victim_sid = sid;
 			task_unlock(p);
 		}
-	}
-	else
+		set_current_cred(cred);
+		break;
+	default:
 		return -EINVAL;
+	}
 
 	return size;
 }
@@ -4669,18 +4756,21 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
 static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
 			     unsigned long flags)
 {
-	struct task_security_struct *tsec = tsk->security;
+	struct cred_security_struct *csec;
 	struct key_security_struct *ksec;
 
 	ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
 	if (!ksec)
 		return -ENOMEM;
 
+	rcu_read_lock();
+	csec = task_cred(tsk)->security;
 	ksec->obj = k;
-	if (tsec->keycreate_sid)
-		ksec->sid = tsec->keycreate_sid;
+	if (csec->keycreate_sid)
+		ksec->sid = csec->keycreate_sid;
 	else
-		ksec->sid = tsec->sid;
+		ksec->sid = csec->action_sid;
+	rcu_read_unlock();
 	k->security = ksec;
 
 	return 0;
@@ -4695,17 +4785,13 @@ static void selinux_key_free(struct key *k)
 }
 
 static int selinux_key_permission(key_ref_t key_ref,
-			    struct task_struct *ctx,
-			    key_perm_t perm)
+				  struct task_struct *ctx,
+				  key_perm_t perm)
 {
 	struct key *key;
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 	struct key_security_struct *ksec;
-
-	key = key_ref_to_ptr(key_ref);
-
-	tsec = ctx->security;
-	ksec = key->security;
+	u32 action_sid;
 
 	/* if no specific permissions are requested, we skip the
 	   permission check. No serious, additional covert channels
@@ -4713,7 +4799,16 @@ static int selinux_key_permission(key_ref_t key_ref,
 	if (perm == 0)
 		return 0;
 
-	return avc_has_perm(tsec->sid, ksec->sid,
+	key = key_ref_to_ptr(key_ref);
+
+	rcu_read_lock();
+	csec = task_cred(ctx)->security;
+	action_sid = csec->action_sid;
+	rcu_read_unlock();
+
+	ksec = key->security;
+
+	return avc_has_perm(action_sid, ksec->sid,
 			    SECCLASS_KEY, perm, NULL);
 }
 
@@ -4788,6 +4883,9 @@ static struct security_operations selinux_ops = {
 	.file_send_sigiotask =		selinux_file_send_sigiotask,
 	.file_receive =			selinux_file_receive,
 
+	.cred_dup =			selinux_cred_dup,
+	.cred_destroy =			selinux_cred_destroy,
+
 	.task_create =			selinux_task_create,
 	.task_alloc_security =		selinux_task_alloc_security,
 	.task_free_security =		selinux_task_free_security,
@@ -4896,6 +4994,17 @@ static struct security_operations selinux_ops = {
 #endif
 };
 
+/*
+ * initial security credentials
+ * - attached to init_cred which is never released
+ */
+static struct cred_security_struct init_cred_sec = {
+	.action_sid	= SECINITSID_KERNEL,
+	.create_sid	= SECINITSID_UNLABELED,
+	.keycreate_sid	= SECINITSID_UNLABELED,
+	.sockcreate_sid	= SECINITSID_UNLABELED,
+};
+
 static __init int selinux_init(void)
 {
 	struct task_security_struct *tsec;
@@ -4907,11 +5016,15 @@ static __init int selinux_init(void)
 
 	printk(KERN_INFO "SELinux:  Initializing.\n");
 
+	/* Set the security state for the initial credentials */
+	init_cred.security = &init_cred_sec;
+	BUG_ON(current->cred != &init_cred);
+
 	/* Set the security state for the initial task. */
 	if (task_alloc_security(current))
 		panic("SELinux:  Failed to initialize initial task.\n");
 	tsec = current->security;
-	tsec->osid = tsec->sid = SECINITSID_KERNEL;
+	tsec->osid = tsec->victim_sid = SECINITSID_KERNEL;
 
 	sel_inode_cache = kmem_cache_create("selinux_inode_security",
 					    sizeof(struct inode_security_struct),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 91b88f0..a1dbc1c 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -27,14 +27,22 @@
 #include "flask.h"
 #include "avc.h"
 
+/*
+ * the security parameters associated with the credentials record structure
+ * (struct cred::security)
+ */
+struct cred_security_struct {
+	u32	action_sid;	/* perform action as SID */
+	u32	create_sid;	/* filesystem object creation as SID */
+	u32	keycreate_sid;	/* key creation as SID */
+	u32	sockcreate_sid;	/* socket creation as SID */
+};
+
 struct task_security_struct {
 	struct task_struct *task;      /* back pointer to task object */
 	u32 osid;            /* SID prior to last execve */
-	u32 sid;             /* current SID */
+	u32 victim_sid;	     /* current SID affecting victimisation of this task */
 	u32 exec_sid;        /* exec SID */
-	u32 create_sid;      /* fscreate SID */
-	u32 keycreate_sid;   /* keycreate SID */
-	u32 sockcreate_sid;  /* fscreate SID */
 	u32 ptrace_sid;      /* SID of ptrace parent */
 };
 
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index c9e92da..9c6737f 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -77,13 +77,13 @@ extern void selnl_notify_setenforce(int val);
 static int task_has_security(struct task_struct *tsk,
 			     u32 perms)
 {
-	struct task_security_struct *tsec;
+	struct cred_security_struct *csec;
 
-	tsec = tsk->security;
-	if (!tsec)
+	csec = tsk->cred->security;
+	if (!csec)
 		return -EACCES;
 
-	return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
+	return avc_has_perm(csec->action_sid, SECINITSID_SECURITY,
 			    SECCLASS_SECURITY, perms, NULL);
 }
 
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index ba715f4..902d302 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -240,7 +240,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
 	/*
 	 * Does the subject have permission to set security context?
 	 */
-	rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+	rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
 			  SECCLASS_ASSOCIATION,
 			  ASSOCIATION__SETCONTEXT, NULL);
 	if (rc)
@@ -341,7 +341,7 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
 	int rc = 0;
 
 	if (ctx)
-		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
 				  SECCLASS_ASSOCIATION,
 				  ASSOCIATION__SETCONTEXT, NULL);
 
@@ -383,7 +383,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
 	int rc = 0;
 
 	if (ctx)
-		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
 				  SECCLASS_ASSOCIATION,
 				  ASSOCIATION__SETCONTEXT, NULL);
 


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-19 16:17 [PATCH 0/3] Introduce credential record David Howells
  2007-09-19 16:17 ` [PATCH 1/3] CRED: Introduce a COW credentials record David Howells
  2007-09-19 16:18 ` [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred David Howells
@ 2007-09-19 16:18 ` David Howells
  2007-09-20  4:11   ` Andrew Morgan
  2007-09-20  8:15   ` David Howells
  2 siblings, 2 replies; 22+ messages in thread
From: David Howells @ 2007-09-19 16:18 UTC (permalink / raw)
  To: viro, hch, Trond.Myklebust, sds, casey
  Cc: linux-kernel, selinux, linux-security-module, dhowells

Move the effective capabilities mask from the task struct into the credentials
record.

Note that the effective capabilities mask in the cred struct shadows that in
the task_struct because a thread can have its capabilities masks changed by
another thread.  The shadowing is performed by update_current_cred() which is
invoked on entry to any system call that might need it.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/buffer.c               |    3 +++
 fs/ioprio.c               |    3 +++
 fs/open.c                 |   27 +++++++++------------------
 fs/proc/array.c           |    2 +-
 fs/readdir.c              |    3 +++
 include/linux/cred.h      |    2 ++
 include/linux/init_task.h |    2 +-
 include/linux/sched.h     |    2 +-
 ipc/msg.c                 |    3 +++
 ipc/sem.c                 |    3 +++
 ipc/shm.c                 |    3 +++
 kernel/acct.c             |    3 +++
 kernel/capability.c       |    3 +++
 kernel/compat.c           |    3 +++
 kernel/cred.c             |   30 +++++++++++++++++++++++-------
 kernel/exit.c             |    2 ++
 kernel/fork.c             |    6 +++++-
 kernel/futex.c            |    3 +++
 kernel/futex_compat.c     |    3 +++
 kernel/kexec.c            |    3 +++
 kernel/module.c           |    6 ++++++
 kernel/ptrace.c           |    3 +++
 kernel/sched.c            |    9 +++++++++
 kernel/signal.c           |    6 ++++++
 kernel/sys.c              |   39 +++++++++++++++++++++++++++++++++++++++
 kernel/sysctl.c           |    3 +++
 kernel/time.c             |    9 +++++++++
 kernel/uid16.c            |    3 +++
 mm/mempolicy.c            |    6 ++++++
 mm/migrate.c              |    3 +++
 mm/mlock.c                |    4 ++++
 mm/mmap.c                 |    3 +++
 mm/mremap.c               |    3 +++
 mm/oom_kill.c             |    9 +++++++--
 mm/swapfile.c             |    6 ++++++
 net/compat.c              |    6 ++++++
 net/socket.c              |   45 +++++++++++++++++++++++++++++++++++++++++++++
 security/dummy.c          |   22 ++++++++++++++++++----
 38 files changed, 259 insertions(+), 35 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 0e5ec37..9aabf79 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2909,6 +2909,9 @@ asmlinkage long sys_bdflush(int func, long data)
 {
 	static int msg_count;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
diff --git a/fs/ioprio.c b/fs/ioprio.c
index 10d2c21..d32b7b7 100644
--- a/fs/ioprio.c
+++ b/fs/ioprio.c
@@ -63,6 +63,9 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
 	struct pid *pgrp;
 	int ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	switch (class) {
 		case IOPRIO_CLASS_RT:
 			if (!capable(CAP_SYS_ADMIN))
diff --git a/fs/open.c b/fs/open.c
index 0c05863..f765ec5 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -450,7 +450,7 @@ out:
 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 {
 	struct nameidata nd;
-	kernel_cap_t old_cap;
+	kernel_cap_t old_cap, want_cap = CAP_EMPTY_SET;
 	struct cred *cred;
 	int res;
 
@@ -461,33 +461,26 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 	if (res < 0)
 		return res;
 
-	old_cap = current->cap_effective;
+	/* Clear the capabilities if we switch to a non-root user */
+	if (!current->uid)
+		want_cap = current->cap_permitted;
+
+	old_cap = current->cred->cap_effective;
 
 	if (current->cred->uid != current->uid ||
-	    current->cred->gid != current->gid) {
+	    current->cred->gid != current->gid ||
+	    current->cred->cap_effective != want_cap) {
 		cred = dup_cred(current->cred);
 		if (!cred)
 			return -ENOMEM;
 
 		change_fsuid(cred, current->uid);
 		change_fsgid(cred, current->gid);
+		change_cap(cred, want_cap);
 	} else {
 		cred = get_current_cred();
 	}
 
-	/*
-	 * Clear the capabilities if we switch to a non-root user
-	 *
-	 * FIXME: There is a race here against sys_capset.  The
-	 * capabilities can change yet we will restore the old
-	 * value below.  We should hold task_capabilities_lock,
-	 * but we cannot because user_path_walk can sleep.
-	 */
-	if (current->uid)
-		cap_clear(current->cap_effective);
-	else
-		current->cap_effective = current->cap_permitted;
-
 	cred = __set_current_cred(cred);
 	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
 	if (res)
@@ -506,8 +499,6 @@ out_path_release:
 	path_release(&nd);
 out:
 	set_current_cred(cred);
-	current->cap_effective = old_cap;
-
 	return res;
 }
 
diff --git a/fs/proc/array.c b/fs/proc/array.c
index dc2f83a..1a406c7 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -286,7 +286,7 @@ static inline char *task_cap(struct task_struct *p, char *buffer)
 			    "CapEff:\t%016x\n",
 			    cap_t(p->cap_inheritable),
 			    cap_t(p->cap_permitted),
-			    cap_t(p->cap_effective));
+			    cap_t(p->_cap_effective));
 }
 
 static inline char *task_context_switch_counts(struct task_struct *p,
diff --git a/fs/readdir.c b/fs/readdir.c
index 57e6aa9..33c69ac 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -103,6 +103,9 @@ asmlinkage long old_readdir(unsigned int fd, struct old_linux_dirent __user * di
 	struct file * file;
 	struct readdir_callback buf;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	error = -EBADF;
 	file = fget(fd);
 	if (!file)
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 6c6feec..a259748 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -24,6 +24,7 @@ struct cred {
 	atomic_t		usage;
 	uid_t			uid;		/* fsuid as was */
 	gid_t			gid;		/* fsgid as was */
+	kernel_cap_t		cap_effective;
 	struct rcu_head		exterminate;	/* cred destroyer */
 	struct group_info	*group_info;
 	void			*security;
@@ -45,6 +46,7 @@ extern int update_current_cred(void);
 extern void put_cred(struct cred *);
 extern void change_fsuid(struct cred *, uid_t);
 extern void change_fsgid(struct cred *, gid_t);
+extern void change_cap(struct cred *, kernel_cap_t);
 extern void change_group_info(struct cred *, struct group_info *);
 extern struct cred *dup_cred(const struct cred *);
 
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index a1882e6..4334137 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -141,7 +141,7 @@ extern struct nsproxy init_nsproxy;
 	.sibling	= LIST_HEAD_INIT(tsk.sibling),			\
 	.group_leader	= &tsk,						\
 	.cred		= &init_cred,					\
-	.cap_effective	= CAP_INIT_EFF_SET,				\
+	._cap_effective	= CAP_INIT_EFF_SET,				\
 	.cap_inheritable = CAP_INIT_INH_SET,				\
 	.cap_permitted	= CAP_FULL_SET,					\
 	.keep_capabilities = 0,						\
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ea85955..edcee93 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1037,7 +1037,7 @@ struct task_struct {
 	struct cred *cred;
 	uid_t uid,euid,suid;
 	gid_t gid,egid,sgid;
-	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
+	kernel_cap_t _cap_effective, cap_inheritable, cap_permitted;
 	unsigned keep_capabilities:1;
 	struct user_struct *user;
 #ifdef CONFIG_KEYS
diff --git a/ipc/msg.c b/ipc/msg.c
index a03fcb5..a351c89 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -393,6 +393,9 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
 	if (msqid < 0 || cmd < 0)
 		return -EINVAL;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	version = ipc_parse_version(&cmd);
 	ns = current->nsproxy->ipc_ns;
 
diff --git a/ipc/sem.c b/ipc/sem.c
index b676fef..9691b40 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -927,6 +927,9 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
 	if (semid < 0)
 		return -EINVAL;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	version = ipc_parse_version(&cmd);
 	ns = current->nsproxy->ipc_ns;
 
diff --git a/ipc/shm.c b/ipc/shm.c
index a86a3a5..709a4fe 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -589,6 +589,9 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
 		goto out;
 	}
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	version = ipc_parse_version(&cmd);
 	ns = current->nsproxy->ipc_ns;
 
diff --git a/kernel/acct.c b/kernel/acct.c
index 24f0f8b..01961a5 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -253,6 +253,9 @@ asmlinkage long sys_acct(const char __user *name)
 {
 	int error;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_PACCT))
 		return -EPERM;
 
diff --git a/kernel/capability.c b/kernel/capability.c
index c8d3c77..3ae73f9 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -178,6 +178,9 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
      int ret;
      pid_t pid;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
      if (get_user(version, &header->version))
 	     return -EFAULT; 
 
diff --git a/kernel/compat.c b/kernel/compat.c
index 3bae374..04be932 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -909,6 +909,9 @@ asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp)
 	struct timex txc;
 	int ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	memset(&txc, 0, sizeof(struct timex));
 
 	if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
diff --git a/kernel/cred.c b/kernel/cred.c
index 6a9dda2..dfc56f2 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -21,16 +21,19 @@
  */
 struct cred init_cred = {
 	.usage		= ATOMIC_INIT(2),
+	.cap_effective	= CAP_INIT_EFF_SET,
 	.group_info	= &init_groups,
 };
 
 /**
  * update_current_cred - Bring the current task's creds up to date
  *
- * Bring the current task's credentials up to date with respect to the keyrings
- * they shadow.  The process and session level keyrings may get changed by
- * sibling threads with the same process, but the change can't be applied back
- * to this thread's cred struct except by this thread itself.
+ * Bring the current task's credential record up to date with respect to the
+ * effective capability mask and keyrings it shadows.  The capabilities mask
+ * may get changed by other processes, and process and session level keyrings
+ * may get changed by sibling threads with the same process, but the change
+ * can't be applied back to this thread's cred struct except by this thread
+ * itself.
  */
 int update_current_cred(void)
 {
@@ -44,16 +47,21 @@ int update_current_cred(void)
 		key_ref_to_ptr(cred->process_keyring) == sig->process_keyring &&
 		key_ref_to_ptr(cred->thread_keyring) == current->thread_keyring &&
 #endif
-		true)
+		cred->cap_effective != current->_cap_effective)
 		return 0;
 
-	cred = kmalloc(sizeof(struct cred), GFP_KERNEL);
+	cred = kmemdup(current->cred, sizeof(struct cred), GFP_KERNEL);
 	if (!cred)
 		return -ENOMEM;
 
-	*cred = *current->cred;
+	if (security_cred_dup(cred) < 0) {
+		kfree(cred);
+		return -ENOMEM;
+	}
+
 	atomic_set(&cred->usage, 1);
 	get_group_info(cred->group_info);
+	cred->cap_effective = current->_cap_effective;
 
 #ifdef CONFIG_KEYS
 	rcu_read_lock();
@@ -156,6 +164,14 @@ void change_fsgid(struct cred *cred, gid_t gid)
 }
 
 /*
+ * change the effective capabilities in a new credential record
+ */
+void change_cap(struct cred *cred, kernel_cap_t cap)
+{
+	cred->cap_effective = cap;
+}
+
+/*
  * change the groups list in a new credential record
  */
 void change_group_info(struct cred *cred, struct group_info *group_info)
diff --git a/kernel/exit.c b/kernel/exit.c
index d1aa7fb..4b3bf36 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -897,6 +897,8 @@ fastcall NORET_TYPE void do_exit(long code)
 	struct task_struct *tsk = current;
 	int group_dead;
 
+	update_current_cred();
+
 	profile_task_exit(tsk);
 
 	WARN_ON(atomic_read(&tsk->fs_excl));
diff --git a/kernel/fork.c b/kernel/fork.c
index 40f8cda..bcc40c4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1422,9 +1422,13 @@ long do_fork(unsigned long clone_flags,
 {
 	struct task_struct *p;
 	int trace = 0;
-	struct pid *pid = alloc_pid();
+	struct pid *pid;
 	long nr;
 
+	if (update_current_cred())
+		return -ENOMEM;
+
+	pid = alloc_pid();
 	if (!pid)
 		return -EAGAIN;
 	nr = pid->nr;
diff --git a/kernel/futex.c b/kernel/futex.c
index e8935b1..40070fe 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1846,6 +1846,9 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
 	struct robust_list_head __user *head;
 	unsigned long ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!pid)
 		head = current->robust_list;
 	else {
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c
index 7e52eb0..a872029 100644
--- a/kernel/futex_compat.c
+++ b/kernel/futex_compat.c
@@ -109,6 +109,9 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
 	struct compat_robust_list_head __user *head;
 	unsigned long ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!pid)
 		head = current->compat_robust_list;
 	else {
diff --git a/kernel/kexec.c b/kernel/kexec.c
index 25db14b..e1feb2f 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -921,6 +921,9 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
 	int locked;
 	int result;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/* We only trust the superuser with rebooting the system. */
 	if (!capable(CAP_SYS_BOOT))
 		return -EPERM;
diff --git a/kernel/module.c b/kernel/module.c
index db0ead0..32893a5 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -660,6 +660,9 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
 	char name[MODULE_NAME_LEN];
 	int ret, forced = 0;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_MODULE))
 		return -EPERM;
 
@@ -1978,6 +1981,9 @@ sys_init_module(void __user *umod,
 	struct module *mod;
 	int ret = 0;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/* Must have permission */
 	if (!capable(CAP_SYS_MODULE))
 		return -EPERM;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 3eca7a5..15fb1ff 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -456,6 +456,9 @@ asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
 	struct task_struct *child;
 	long ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/*
 	 * This lock_kernel fixes a subtle race with suid exec
 	 */
diff --git a/kernel/sched.c b/kernel/sched.c
index deeb1f8..0ca5e42 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -4058,6 +4058,9 @@ asmlinkage long sys_nice(int increment)
 {
 	long nice, retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/*
 	 * Setpriority might change our priority at the same moment.
 	 * We don't have to worry. Conceptually one call occurs first
@@ -4290,6 +4293,9 @@ do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
 	struct task_struct *p;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!param || pid < 0)
 		return -EINVAL;
 	if (copy_from_user(&lparam, param, sizeof(struct sched_param)))
@@ -4463,6 +4469,9 @@ asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len,
 	cpumask_t new_mask;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = get_user_cpu_mask(user_mask_ptr, len, &new_mask);
 	if (retval)
 		return retval;
diff --git a/kernel/signal.c b/kernel/signal.c
index 3169bed..84bd469 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2199,6 +2199,9 @@ sys_kill(int pid, int sig)
 {
 	struct siginfo info;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	info.si_signo = sig;
 	info.si_errno = 0;
 	info.si_code = SI_USER;
@@ -2214,6 +2217,9 @@ static int do_tkill(int tgid, int pid, int sig)
 	struct siginfo info;
 	struct task_struct *p;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	error = -ESRCH;
 	info.si_signo = sig;
 	info.si_errno = 0;
diff --git a/kernel/sys.c b/kernel/sys.c
index 7f15e9f..3cd095b 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -670,6 +670,9 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
 	int error = -EINVAL;
 	struct pid *pgrp;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (which > PRIO_USER || which < PRIO_PROCESS)
 		goto out;
 
@@ -896,6 +899,9 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
 {
 	char buffer[256];
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/* We only trust the superuser with rebooting the system. */
 	if (!capable(CAP_SYS_BOOT))
 		return -EPERM;
@@ -1019,6 +1025,9 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
 	int new_egid = old_egid;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
 	if (retval)
 		return retval;
@@ -1072,6 +1081,9 @@ asmlinkage long sys_setgid(gid_t gid)
 	int old_egid = current->egid;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
 	if (retval)
 		return retval;
@@ -1150,6 +1162,9 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
 	int old_ruid, old_euid, old_suid, new_ruid, new_euid;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
 	if (retval)
 		return retval;
@@ -1221,6 +1236,9 @@ asmlinkage long sys_setuid(uid_t uid)
 	int old_ruid, old_suid, new_suid;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
 	if (retval)
 		return retval;
@@ -1271,6 +1289,9 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 	int old_suid = current->suid;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
 	if (retval)
 		return retval;
@@ -1333,6 +1354,9 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 	struct cred *cred;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
 	if (retval)
 		return retval;
@@ -1876,6 +1900,9 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
 	struct group_info *group_info;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SETGID))
 		return -EPERM;
 	if ((unsigned)gidsetsize > NGROUPS_MAX)
@@ -1941,6 +1968,9 @@ asmlinkage long sys_sethostname(char __user *name, int len)
 	int errno;
 	char tmp[__NEW_UTS_LEN];
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	if (len < 0 || len > __NEW_UTS_LEN)
@@ -1986,6 +2016,9 @@ asmlinkage long sys_setdomainname(char __user *name, int len)
 	int errno;
 	char tmp[__NEW_UTS_LEN];
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	if (len < 0 || len > __NEW_UTS_LEN)
@@ -2045,6 +2078,9 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
 	unsigned long it_prof_secs;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (resource >= RLIM_NLIMITS)
 		return -EINVAL;
 	if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
@@ -2226,6 +2262,9 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
 {
 	long error;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	error = security_task_prctl(option, arg2, arg3, arg4, arg5);
 	if (error)
 		return error;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 6ace893..4f52c1f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1339,6 +1339,9 @@ asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
 	struct __sysctl_args tmp;
 	int error;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (copy_from_user(&tmp, args, sizeof(tmp)))
 		return -EFAULT;
 
diff --git a/kernel/time.c b/kernel/time.c
index 2289a8d..975f47d 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -82,6 +82,9 @@ asmlinkage long sys_stime(time_t __user *tptr)
 	struct timespec tv;
 	int err;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (get_user(tv.tv_sec, tptr))
 		return -EFAULT;
 
@@ -186,6 +189,9 @@ asmlinkage long sys_settimeofday(struct timeval __user *tv,
 	struct timespec	new_ts;
 	struct timezone new_tz;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (tv) {
 		if (copy_from_user(&user_tv, tv, sizeof(*tv)))
 			return -EFAULT;
@@ -205,6 +211,9 @@ asmlinkage long sys_adjtimex(struct timex __user *txc_p)
 	struct timex txc;		/* Local copy of parameter */
 	int ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/* Copy the user data space into the kernel copy
 	 * structure. But bear in mind that the structures
 	 * may change
diff --git a/kernel/uid16.c b/kernel/uid16.c
index 5a8b95e..5238a96 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -187,6 +187,9 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
 	struct group_info *group_info;
 	int retval;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SETGID))
 		return -EPERM;
 	if ((unsigned)gidsetsize > NGROUPS_MAX)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index bb54b88..e9f7b32 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -878,6 +878,9 @@ asmlinkage long sys_mbind(unsigned long start, unsigned long len,
 	nodemask_t nodes;
 	int err;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	err = get_nodes(&nodes, nmask, maxnode);
 	if (err)
 		return err;
@@ -914,6 +917,9 @@ asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
 	nodemask_t task_nodes;
 	int err;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	err = get_nodes(&old, old_nodes, maxnode);
 	if (err)
 		return err;
diff --git a/mm/migrate.c b/mm/migrate.c
index e2fdbce..79a1909 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -915,6 +915,9 @@ asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages,
 	struct mm_struct *mm;
 	struct page_to_node *pm = NULL;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/* Check flags */
 	if (flags & ~(MPOL_MF_MOVE|MPOL_MF_MOVE_ALL))
 		return -EINVAL;
diff --git a/mm/mlock.c b/mm/mlock.c
index 7b26560..67985f4 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -138,6 +138,8 @@ asmlinkage long sys_mlock(unsigned long start, size_t len)
 	unsigned long lock_limit;
 	int error = -ENOMEM;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
 	if (!can_do_mlock())
 		return -EPERM;
 
@@ -203,6 +205,8 @@ asmlinkage long sys_mlockall(int flags)
 	if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
 		goto out;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
 	ret = -EPERM;
 	if (!can_do_mlock())
 		goto out;
diff --git a/mm/mmap.c b/mm/mmap.c
index 0d40e66..1b7b0ff 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -240,6 +240,9 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
 	unsigned long newbrk, oldbrk;
 	struct mm_struct *mm = current->mm;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	down_write(&mm->mmap_sem);
 
 	if (brk < mm->end_code)
diff --git a/mm/mremap.c b/mm/mremap.c
index 8ea5c24..0d49048 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -418,6 +418,9 @@ asmlinkage unsigned long sys_mremap(unsigned long addr,
 {
 	unsigned long ret;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	down_write(&current->mm->mmap_sem);
 	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
 	up_write(&current->mm->mmap_sem);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f9b82ad..df5edda 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -53,6 +53,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
 	unsigned long points, cpu_time, run_time, s;
 	struct mm_struct *mm;
 	struct task_struct *child;
+	kernel_cap_t cap_effective;
 
 	task_lock(p);
 	mm = p->mm;
@@ -123,7 +124,11 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
 	 * Superuser processes are usually more important, so we make it
 	 * less likely that we kill those.
 	 */
-	if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_ADMIN) ||
+	rcu_read_lock();
+	cap_effective = task_cred(p)->cap_effective;
+	rcu_read_unlock();
+
+	if (cap_t(cap_effective) & CAP_TO_MASK(CAP_SYS_ADMIN) ||
 				p->uid == 0 || p->euid == 0)
 		points /= 4;
 
@@ -133,7 +138,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
 	 * tend to only have this flag set on applications they think
 	 * of as important.
 	 */
-	if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO))
+	if (cap_t(cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO))
 		points /= 4;
 
 	/*
diff --git a/mm/swapfile.c b/mm/swapfile.c
index f071648..9539da4 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1183,6 +1183,9 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
 	int i, type, prev;
 	int err;
 	
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
@@ -1433,6 +1436,9 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
 	struct inode *inode = NULL;
 	int did_down = 0;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	spin_lock(&swap_lock);
diff --git a/net/compat.c b/net/compat.c
index d74d821..c20f404 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -483,6 +483,9 @@ asmlinkage long compat_sys_setsockopt(int fd, int level, int optname,
 	int err;
 	struct socket *sock;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (level == SOL_IPV6 && optname == IPT_SO_SET_REPLACE)
 		return do_netfilter_replace(fd, level, optname,
 					    optval, optlen);
@@ -603,6 +606,9 @@ asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
 	int err;
 	struct socket *sock;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if ((sock = sockfd_lookup(fd, &err))!=NULL)
 	{
 		err = security_socket_getsockopt(sock, level,
diff --git a/net/socket.c b/net/socket.c
index 50bfeef..034d221 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1200,6 +1200,9 @@ asmlinkage long sys_socket(int family, int type, int protocol)
 	int retval;
 	struct socket *sock;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	retval = sock_create(family, type, protocol, &sock);
 	if (retval < 0)
 		goto out;
@@ -1228,6 +1231,9 @@ asmlinkage long sys_socketpair(int family, int type, int protocol,
 	int fd1, fd2, err;
 	struct file *newfile1, *newfile2;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	/*
 	 * Obtain the first socket and check if the underlying protocol
 	 * supports the socketpair call.
@@ -1323,6 +1329,9 @@ asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
 	char address[MAX_SOCK_ADDR];
 	int err, fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (sock) {
 		err = move_addr_to_kernel(umyaddr, addrlen, address);
@@ -1353,6 +1362,9 @@ asmlinkage long sys_listen(int fd, int backlog)
 	struct socket *sock;
 	int err, fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (sock) {
 		if ((unsigned)backlog > sysctl_somaxconn)
@@ -1387,6 +1399,9 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr,
 	int err, len, newfd, fput_needed;
 	char address[MAX_SOCK_ADDR];
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (!sock)
 		goto out;
@@ -1476,6 +1491,9 @@ asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr,
 	char address[MAX_SOCK_ADDR];
 	int err, fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (!sock)
 		goto out;
@@ -1508,6 +1526,9 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr,
 	char address[MAX_SOCK_ADDR];
 	int len, err, fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (!sock)
 		goto out;
@@ -1539,6 +1560,9 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr,
 	char address[MAX_SOCK_ADDR];
 	int len, err, fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (sock != NULL) {
 		err = security_socket_getpeername(sock);
@@ -1576,6 +1600,9 @@ asmlinkage long sys_sendto(int fd, void __user *buff, size_t len,
 	int fput_needed;
 	struct file *sock_file;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock_file = fget_light(fd, &fput_needed);
 	err = -EBADF;
 	if (!sock_file)
@@ -1637,6 +1664,9 @@ asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size,
 	struct file *sock_file;
 	int fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock_file = fget_light(fd, &fput_needed);
 	err = -EBADF;
 	if (!sock_file)
@@ -1693,6 +1723,9 @@ asmlinkage long sys_setsockopt(int fd, int level, int optname,
 	if (optlen < 0)
 		return -EINVAL;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (sock != NULL) {
 		err = security_socket_setsockopt(sock, level, optname);
@@ -1724,6 +1757,9 @@ asmlinkage long sys_getsockopt(int fd, int level, int optname,
 	int err, fput_needed;
 	struct socket *sock;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (sock != NULL) {
 		err = security_socket_getsockopt(sock, level, optname);
@@ -1753,6 +1789,9 @@ asmlinkage long sys_shutdown(int fd, int how)
 	int err, fput_needed;
 	struct socket *sock;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	sock = sockfd_lookup_light(fd, &err, &fput_needed);
 	if (sock != NULL) {
 		err = security_socket_shutdown(sock, how);
@@ -1789,6 +1828,9 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
 	int err, ctl_len, iov_size, total_len;
 	int fput_needed;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	err = -EFAULT;
 	if (MSG_CMSG_COMPAT & flags) {
 		if (get_compat_msghdr(&msg_sys, msg_compat))
@@ -1896,6 +1938,9 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg,
 	struct sockaddr __user *uaddr;
 	int __user *uaddr_len;
 
+	if (update_current_cred() < 0)
+		return -ENOMEM;
+
 	if (MSG_CMSG_COMPAT & flags) {
 		if (get_compat_msghdr(&msg_sys, msg_compat))
 			return -EFAULT;
diff --git a/security/dummy.c b/security/dummy.c
index f535cc6..f403658 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -76,7 +76,13 @@ static int dummy_acct (struct file *file)
 
 static int dummy_capable (struct task_struct *tsk, int cap)
 {
-	if (cap_raised (tsk->cap_effective, cap))
+	kernel_cap_t cap_effective;
+
+	rcu_read_lock();
+	cap_effective = task_cred(tsk)->cap_effective;
+	rcu_read_unlock();
+
+	if (cap_raised (cap_effective, cap))
 		return 0;
 	return -EPERM;
 }
@@ -146,7 +152,12 @@ static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
 	change_fsuid(bprm->cred, bprm->e_uid);
 	change_fsgid(bprm->cred, bprm->e_gid);
 
-	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
+	dummy_capget(current,
+		     &current->_cap_effective,
+		     &current->cap_inheritable,
+		     &current->cap_permitted);
+
+	change_cap(bprm->cred, current->_cap_effective);
 }
 
 static void dummy_bprm_post_apply_creds (struct linux_binprm *bprm)
@@ -499,7 +510,10 @@ static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
 
 static int dummy_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
 {
-	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
+	dummy_capget(current,
+		     &current->_cap_effective,
+		     &current->cap_inheritable,
+		     &current->cap_permitted);
 	return 0;
 }
 
@@ -696,7 +710,7 @@ static int dummy_sem_semop (struct sem_array *sma,
 
 static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb)
 {
-	NETLINK_CB(skb).eff_cap = current->cap_effective;
+	NETLINK_CB(skb).eff_cap = current->cred->cap_effective;
 	return 0;
 }
 


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-19 16:18 ` [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred David Howells
@ 2007-09-19 17:28   ` Casey Schaufler
  2007-09-19 22:57     ` David Howells
  2007-09-24 14:00   ` Serge E. Hallyn
  1 sibling, 1 reply; 22+ messages in thread
From: Casey Schaufler @ 2007-09-19 17:28 UTC (permalink / raw)
  To: David Howells, viro, hch, Trond.Myklebust, sds, casey
  Cc: linux-kernel, selinux, linux-security-module, dhowells


--- David Howells <dhowells@redhat.com> wrote:

> Move into the cred struct the part of the task security data that defines how
> a
> task acts upon an object.  The part that defines how something acts upon a
> task
> remains attached to the task.

This seems to me to be an unnatural and inappropriate separation.
Move the whole of the security blob into the cred if you must have
a cred (which I was soooo glad Linux didn't have after having dealt
with it in Solaris) rather than having two blobs to deal with. If an
LSM requires a different treatment between when a task is a subject
and when it is an object the LSM should handle that itself.

> For SELinux this requires some of task_security_struct to be split off into
> cred_security_struct which is then attached to struct cred.  Note that the
> contents of cred_security_struct may not be changed without the generation of
> a
> new struct cred.
> 
> The split is as follows:
> 
>  (*) create_sid, keycreate_sid and sockcreate_sid just move across.
> 
>  (*) sid is split into victim_sid - which remains - and action_sid - which
>      migrates.
> 
>  (*) osid, exec_sid and ptrace_sid remain.
> 
> victim_sid is the SID used to govern actions upon the task.  action_sid is
> used
> to govern actions made by the task.

So put all these fields into one blob and attach them to the cred.
Actually, if you put all these fields in the task blob maybe you
don't need to do your COW thing at all.
 



Casey Schaufler
casey@schaufler-ca.com

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-19 17:28   ` Casey Schaufler
@ 2007-09-19 22:57     ` David Howells
  2007-09-20 16:31       ` Casey Schaufler
  0 siblings, 1 reply; 22+ messages in thread
From: David Howells @ 2007-09-19 22:57 UTC (permalink / raw)
  To: casey
  Cc: dhowells, viro, hch, Trond.Myklebust, sds, linux-kernel, selinux,
	linux-security-module

Casey Schaufler <casey@schaufler-ca.com> wrote:

> > Move into the cred struct the part of the task security data that defines
> > how a task acts upon an object.  The part that defines how something acts
> > upon a task remains attached to the task.
> 
> This seems to me to be an unnatural and inappropriate separation.  Move the
> whole of the security blob into the cred if you must have a cred (which I
> was soooo glad Linux didn't have after having dealt with it in Solaris)
> rather than having two blobs to deal with.

The separation is necessary for a few reasons:

 (1) The task victimisation context must *not* be changed by a temporary
     override of the action and creation contexts for purposes such as
     cachefiles.

 (2) If the victimisation context is not included in the override cred, then I
     only need one copy of the override cred to do *all* the work for
     cachefiles.  I can share that singular override blob across every task
     that wishes to access the cache.

 (3) If the victimisation context is moved to the override cred, I have to
     create a new context every time I want to apply the override.  This means
     I have to deal with the possibility of OOM at such points.  I could cache
     the contexts, but that's messy - and unnecessary.

> If an LSM requires a different treatment between when a task is a subject and
> when it is an object the LSM should handle that itself.

Indeed, but I can help it to do so by providing separate security pointers on
the task struct and the cred struct.

> So put all these fields into one blob and attach them to the cred.

The separation is, I think, the correct thing to do.

> Actually, if you put all these fields in the task blob maybe you
> don't need to do your COW thing at all.

Whilst that is true, one of the purposes of this is to make it easier and
cleaner to effect the override.  Every field in the cred struct potentially
must be overridden.  That's a lot of context to save each time I need to apply
the override and a lot of context to restore each time I want to restore it.

With these patches, all I need to do is to take a ref and swap the cred
pointers with a memory barrier to satisfy the RCU, and then swap them back
again and release the ref.  It's much, much simpler.

Furthermore, with respect to LSM and SELinux, I think I can remove the SELinux
specific knowledge currently present in cachefiles by saying to LSM "give me a
cred for kernel service X".  With SELinux this can do all the transformations
necessary to give me the appropriate action SID and file creation SID without
me needing to know that these concepts exist.  I just apply the cred I'm given
as an override.

With your suggestion, I either have to do a full set of transformations each
time I want to apply the override, or I have to know about SELinux or
whatever's internals.  Your objection to my earlier patch was this very point.

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-19 16:18 ` [PATCH 3/3] CRED: Move the effective capabilities into the cred struct David Howells
@ 2007-09-20  4:11   ` Andrew Morgan
  2007-09-20 13:38     ` Trond Myklebust
  2007-09-26 18:23     ` Al Viro
  2007-09-20  8:15   ` David Howells
  1 sibling, 2 replies; 22+ messages in thread
From: Andrew Morgan @ 2007-09-20  4:11 UTC (permalink / raw)
  To: David Howells
  Cc: viro, hch, Trond.Myklebust, sds, casey, linux-kernel, selinux,
	linux-security-module

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

David Howells wrote:
> Move the effective capabilities mask from the task struct into the credentials
> record.
> 
> Note that the effective capabilities mask in the cred struct shadows that in
> the task_struct because a thread can have its capabilities masks changed by
> another thread.  The shadowing is performed by update_current_cred() which is
> invoked on entry to any system call that might need it.

OOC If we were to simply drop support for one process changing the
capabilities of another, would we need this patch?

Thanks

Andrew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQFG8fLrQheEq9QabfIRApPOAKCHAoazhTTpY/qSjdmRZxDptqeqiACfd4Q7
mdIPx+xpG19ih9uiVv1NSBU=
=TfZd
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-19 16:18 ` [PATCH 3/3] CRED: Move the effective capabilities into the cred struct David Howells
  2007-09-20  4:11   ` Andrew Morgan
@ 2007-09-20  8:15   ` David Howells
  1 sibling, 0 replies; 22+ messages in thread
From: David Howells @ 2007-09-20  8:15 UTC (permalink / raw)
  To: Andrew Morgan
  Cc: dhowells, viro, hch, Trond.Myklebust, sds, casey, linux-kernel,
	selinux, linux-security-module

Andrew Morgan <morgan@kernel.org> wrote:

> OOC If we were to simply drop support for one process changing the
> capabilities of another, would we need this patch?

Well, the patch could be less, but there's still the possibility of a kernel
service wanting to override the capabilities mask.

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-20  4:11   ` Andrew Morgan
@ 2007-09-20 13:38     ` Trond Myklebust
  2007-09-20 15:36       ` Casey Schaufler
  2007-09-26 18:23     ` Al Viro
  1 sibling, 1 reply; 22+ messages in thread
From: Trond Myklebust @ 2007-09-20 13:38 UTC (permalink / raw)
  To: Andrew Morgan
  Cc: David Howells, viro, hch, sds, casey, linux-kernel, selinux,
	linux-security-module

On Wed, 2007-09-19 at 21:11 -0700, Andrew Morgan wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> David Howells wrote:
> > Move the effective capabilities mask from the task struct into the credentials
> > record.
> > 
> > Note that the effective capabilities mask in the cred struct shadows that in
> > the task_struct because a thread can have its capabilities masks changed by
> > another thread.  The shadowing is performed by update_current_cred() which is
> > invoked on entry to any system call that might need it.
> 
> OOC If we were to simply drop support for one process changing the
> capabilities of another, would we need this patch?

No. This has nothing to do about one process changing some other
process' capabilities. It has to do with being able to pass security
information around the kernel beyond the confines of the task struct.

This is needed in order to deal with asynchronous i/o where security
checks may have to be deferred, and where the task struct may no longer
be available.
One example would be a failover situation when doing deferred writes: if
the first choice of storage medium is unavailable, and the kernel tries
to fail the write over to another storage. On NFS that might involve
having to build up a new RPCSEC_GSS security context for the new server.
Currently, you cannot do this safely because all the security info is
cached in the task struct and much of it cannot be copied.

Trond

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-20 13:38     ` Trond Myklebust
@ 2007-09-20 15:36       ` Casey Schaufler
  2007-09-20 16:09         ` Trond Myklebust
  0 siblings, 1 reply; 22+ messages in thread
From: Casey Schaufler @ 2007-09-20 15:36 UTC (permalink / raw)
  To: Trond Myklebust, Andrew Morgan
  Cc: David Howells, viro, hch, sds, casey, linux-kernel, selinux,
	linux-security-module


--- Trond Myklebust <Trond.Myklebust@netapp.com> wrote:

> On Wed, 2007-09-19 at 21:11 -0700, Andrew Morgan wrote:
> > -----BEGIN PGP SIGNED MESSAGE-----
> > Hash: SHA1
> > 
> > David Howells wrote:
> > > Move the effective capabilities mask from the task struct into the
> credentials
> > > record.
> > > 
> > > Note that the effective capabilities mask in the cred struct shadows that
> in
> > > the task_struct because a thread can have its capabilities masks changed
> by
> > > another thread.  The shadowing is performed by update_current_cred()
> which is
> > > invoked on entry to any system call that might need it.
> > 
> > OOC If we were to simply drop support for one process changing the
> > capabilities of another, would we need this patch?
> 
> No. This has nothing to do about one process changing some other
> process' capabilities. It has to do with being able to pass security
> information around the kernel beyond the confines of the task struct.
> 
> This is needed in order to deal with asynchronous i/o where security
> checks may have to be deferred, and where the task struct may no longer
> be available.
> One example would be a failover situation when doing deferred writes: if
> the first choice of storage medium is unavailable, and the kernel tries
> to fail the write over to another storage. On NFS that might involve
> having to build up a new RPCSEC_GSS security context for the new server.
> Currently, you cannot do this safely because all the security info is
> cached in the task struct and much of it cannot be copied.

Ok, what can't be copied, and why can't it be copied?


Casey Schaufler
casey@schaufler-ca.com

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-20 15:36       ` Casey Schaufler
@ 2007-09-20 16:09         ` Trond Myklebust
  0 siblings, 0 replies; 22+ messages in thread
From: Trond Myklebust @ 2007-09-20 16:09 UTC (permalink / raw)
  To: casey
  Cc: Andrew Morgan, David Howells, viro, hch, sds, linux-kernel,
	selinux, linux-security-module

On Thu, 2007-09-20 at 08:36 -0700, Casey Schaufler wrote:

> Ok, what can't be copied, and why can't it be copied?

In practice, no security information can be copied because the checks
are all made on the "current" pointer. There is no mechanism other than
'current' for passing security information around.

Trond

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-19 22:57     ` David Howells
@ 2007-09-20 16:31       ` Casey Schaufler
  2007-09-20 17:17         ` David Howells
  2007-09-20 17:33         ` David Howells
  0 siblings, 2 replies; 22+ messages in thread
From: Casey Schaufler @ 2007-09-20 16:31 UTC (permalink / raw)
  To: David Howells, casey
  Cc: dhowells, viro, hch, Trond.Myklebust, sds, linux-kernel, selinux,
	linux-security-module


--- David Howells <dhowells@redhat.com> wrote:

> Casey Schaufler <casey@schaufler-ca.com> wrote:
> 
> > > Move into the cred struct the part of the task security data that defines
> > > how a task acts upon an object.  The part that defines how something acts
> > > upon a task remains attached to the task.
> > 
> > This seems to me to be an unnatural and inappropriate separation.  Move the
> > whole of the security blob into the cred if you must have a cred (which I
> > was soooo glad Linux didn't have after having dealt with it in Solaris)
> > rather than having two blobs to deal with. 

Could you use "object context" instead of "victimisation context"?
It would be in better keeping with traditional security jargon.

> The separation is necessary for a few reasons:
> 
>  (1) The task victimisation context must *not* be changed by a temporary
>      override of the action and creation contexts for purposes such as
>      cachefiles.

That would be for the LSM to decide, not the file system. While I
concede that it is unlikely that you are going to want to use the
same security attributes for your "object" and your "subject" I also
suggest that it is probable that the "object" attributes will want to
change in the case of an filesystem daemon as well, and that the
filesystem code by itself can't know what's correct.

>  (2) If the victimisation context is not included in the override cred, then
> I
>      only need one copy of the override cred to do *all* the work for
>      cachefiles.  I can share that singular override blob across every task
>      that wishes to access the cache.

Assuming that the LSM goes along with the notion you could do that
with a task struct, too.

>  (3) If the victimisation context is moved to the override cred, I have to
>      create a new context every time I want to apply the override.  This
> means
>      I have to deal with the possibility of OOM at such points.  I could
> cache
>      the contexts, but that's messy - and unnecessary.

You're making a big mess (that's my opinion, take it for what it's worth)
throughout the LSMs to deal with a single (or maybe a very few) special
case.

> > If an LSM requires a different treatment between when a task is a subject
> and
> > when it is an object the LSM should handle that itself.
> 
> Indeed, but I can help it to do so by providing separate security pointers on
> the task struct and the cred struct.
> 
> > So put all these fields into one blob and attach them to the cred.
> 
> The separation is, I think, the correct thing to do.
> 
> > Actually, if you put all these fields in the task blob maybe you
> > don't need to do your COW thing at all.
> 
> Whilst that is true,

Glad I figured it out.

> one of the purposes of this is to make it easier and
> cleaner to effect the override.  Every field in the cred struct potentially
> must be overridden.  That's a lot of context to save each time I need to
> apply
> the override and a lot of context to restore each time I want to restore it.

So it sounds like what you'd be happiest with would be a separate task
struct hand crafted to he the right "object" and "subject" attributes.
Couldn't you set up a task to do the overridden operations? Yes, it
would have it's own set of ugly, but it would be isolated. I haven't
been through the code of late, but this used to be what nfsd did, that
being nothing except loaning it's attributes (ok, it was a cred) to
whoever needed them.

> With these patches, all I need to do is to take a ref and swap the cred
> pointers with a memory barrier to satisfy the RCU, and then swap them back
> again and release the ref.  It's much, much simpler.

Yes, but the LSM writer now has to maintain two full security blobs
even if the system doesn't want cachefs or nfs.

> Furthermore, with respect to LSM and SELinux, I think I can remove the
> SELinux
> specific knowledge currently present in cachefiles by saying to LSM "give me
> a
> cred for kernel service X".  With SELinux this can do all the transformations
> necessary to give me the appropriate action SID and file creation SID without
> me needing to know that these concepts exist.  I just apply the cred I'm
> given
> as an override.

You could have done the same thing passing the task instead of a cred.

> With your suggestion, I either have to do a full set of transformations each
> time I want to apply the override, or I have to know about SELinux or
> whatever's internals.  Your objection to my earlier patch was this very
> point.

Yup. And I'm reluctantly withdrawing my objection to exporting secid's
across the LSM interface. I didn't like 'em when I saw 'em in 1988 in
the SecureWare CMW, and I don't like 'em any better now, but I had three
tries at extracting them from use outside the SELinux specific code and
it's clear that there's no way to do it without being Linus. For Smack
I have restructured a couple lists and can deal with secid's now.

I don't see any way to get around the LSM being involved, even with
secid's. Only the LSM can decide what is appropriate for an override
value. Maybe all you need is

    int security_task_godlikesecid(u32 *secid)

which gives you the fully protected, all powerfull secid. That doesn't
handle capabilities, of course, but It appears you know how to deal with
those already.

I appologize that I can't offer a complete alternative at this point,
but I do have other fish on the barbie right now. Thank you for your
attention to my concerns.




Casey Schaufler
casey@schaufler-ca.com

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-20 16:31       ` Casey Schaufler
@ 2007-09-20 17:17         ` David Howells
  2007-09-20 17:33         ` David Howells
  1 sibling, 0 replies; 22+ messages in thread
From: David Howells @ 2007-09-20 17:17 UTC (permalink / raw)
  To: casey
  Cc: dhowells, viro, hch, Trond.Myklebust, sds, linux-kernel, selinux,
	linux-security-module

Casey Schaufler <casey@schaufler-ca.com> wrote:

> Could you use "object context" instead of "victimisation context"?
> It would be in better keeping with traditional security jargon.

If you wish.  Personally, I find it clearer than object or subject.

> > The separation is necessary for a few reasons:
> > 
> >  (1) The task victimisation context must *not* be changed by a temporary
> >      override of the action and creation contexts for purposes such as
> >      cachefiles.
> 
> That would be for the LSM to decide, not the file system.

Filesystem?  CacheFiles is acting on behalf of a filesystem and must override
the context that that filesystem was using, so that it can access a different
filesystem.

> While I concede that it is unlikely that you are going to want to use the
> same security attributes for your "object" and your "subject"

99.99% probable that I will want a different subjective context for talking to
the cache to the objective context of the task I'm doing work on behalf of.

> I also suggest that it is probable that the "object" attributes will want to
> change in the case of an filesystem daemon as well, 

What daemon are you referring to?

You've misunderstood, I think.  Consider readpages.  CacheFiles really wants to
run in the process context of whoever called readpages.  The main reason for
this is one of performance: readpages() is called an awful lot, and we don't
want to be changing process context at least twice per call if we can avoid it.

> and that the filesystem code by itself can't know what's correct.

Indeed.  I have mentioned that I intend to create a patch to provide an LSM
hook by which a kernel service can ask the LSM module for a new cred struct,
appropriate to that service.  That way, all details of SIDs, secids,
transformations, etc are hidden inside the LSM module.

> Assuming that the LSM goes along with the notion you could do that
> with a task struct, too.

Sigh.  Only by saving and restoring the complete credential context from/to the
task struct.

> So it sounds like what you'd be happiest with would be a separate task
> struct hand crafted to he the right "object" and "subject" attributes.

Exchanging task structs is highly impractical, I think.

> Couldn't you set up a task to do the overridden operations?

Performance.

> Yes, but the LSM writer now has to maintain two full security blobs
> even if the system doesn't want cachefs or nfs.

This is actually the simpler and cleaner solution.  It has been assumed,
generally, to this point that subjective context == objective context, but we
have two cases where we need to break that assumption.

> You could have done the same thing passing the task instead of a cred.

? I didn't mention passing a cred, did I?

> And I'm reluctantly withdrawing my objection to exporting secid's across the
> LSM interface

Actually, I think you were right.  I shouldn't be exposing secids like that.
There's no requirement for an LSM to have such a concept.

> I don't see any way to get around the LSM being involved, even with
> secid's.

Of course the LSM must be involved, but secids aren't needed.

> Only the LSM can decide what is appropriate for an override value. Maybe all
> you need is
> 
>     int security_task_godlikesecid(u32 *secid)

No, all I really need is something like:

	struct cred *security_get_kernel_service_cred(const char *name);

And maybe:

	int security_create_files_as(struct cred *, struct inode *);

So that I can say I want to be able to create files that look the same as that
inode there.  The LSM can then check that the subjective context in the cred
struct is allowed to do that.

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-20 16:31       ` Casey Schaufler
  2007-09-20 17:17         ` David Howells
@ 2007-09-20 17:33         ` David Howells
  1 sibling, 0 replies; 22+ messages in thread
From: David Howells @ 2007-09-20 17:33 UTC (permalink / raw)
  Cc: dhowells, casey, viro, hch, Trond.Myklebust, sds, linux-kernel,
	selinux, linux-security-module

David Howells <dhowells@redhat.com> wrote:

> > So it sounds like what you'd be happiest with would be a separate task
> > struct hand crafted to he the right "object" and "subject" attributes.

Actually, that whilst that is sort of feasible for CacheFiles[*], it is not
really feasible for NFSd.  NFSd possesses a set of daemons that have a
standard objective context and substitute a lot of different subjective
contexts as they perform VFS operations on behalf of remote clients.

  [*] But in practice quite icky from other points of view.

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-19 16:18 ` [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred David Howells
  2007-09-19 17:28   ` Casey Schaufler
@ 2007-09-24 14:00   ` Serge E. Hallyn
  2007-09-24 14:21     ` Stephen Smalley
  2007-09-26 13:30     ` David Howells
  1 sibling, 2 replies; 22+ messages in thread
From: Serge E. Hallyn @ 2007-09-24 14:00 UTC (permalink / raw)
  To: David Howells
  Cc: viro, hch, Trond.Myklebust, sds, casey, linux-kernel, selinux,
	linux-security-module

Quoting David Howells (dhowells@redhat.com):
> Move into the cred struct the part of the task security data that defines how a
> task acts upon an object.  The part that defines how something acts upon a task
> remains attached to the task.
> 
> For SELinux this requires some of task_security_struct to be split off into
> cred_security_struct which is then attached to struct cred.  Note that the
> contents of cred_security_struct may not be changed without the generation of a
> new struct cred.
> 
> The split is as follows:
> 
>  (*) create_sid, keycreate_sid and sockcreate_sid just move across.
> 
>  (*) sid is split into victim_sid - which remains - and action_sid - which
>      migrates.

My concern is with this victim_sid.  Whether the concern is valid
depends on exactly how the other credentials can be used, which isn't
yet entirely clear to me.

So my concern is that while a task is acting with alternate creds,
another task can act upon it based upon victim_sid.  So does this
open up the possibility for an unprivileged task to ptrace or kill
a task in the middle of a privileged operation?  Is that somehow
safe in the way this is used here?

I guess I need to look more at the actual nfs patches etc.

thanks,
-serge


>  (*) osid, exec_sid and ptrace_sid remain.
> 
> victim_sid is the SID used to govern actions upon the task.  action_sid is used
> to govern actions made by the task.
> 
> When accessing the cred_security_struct of another process, RCU read procedures
> must be observed.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
> 
>  include/linux/cred.h              |    1 
>  include/linux/security.h          |   34 +++
>  kernel/cred.c                     |    7 +
>  security/dummy.c                  |   11 +
>  security/selinux/exports.c        |    6 
>  security/selinux/hooks.c          |  497 +++++++++++++++++++++++--------------
>  security/selinux/include/objsec.h |   16 +
>  security/selinux/selinuxfs.c      |    8 -
>  security/selinux/xfrm.c           |    6 
>  9 files changed, 380 insertions(+), 206 deletions(-)
> 
> diff --git a/include/linux/cred.h b/include/linux/cred.h
> index 22ae610..6c6feec 100644
> --- a/include/linux/cred.h
> +++ b/include/linux/cred.h
> @@ -26,6 +26,7 @@ struct cred {
>  	gid_t			gid;		/* fsgid as was */
>  	struct rcu_head		exterminate;	/* cred destroyer */
>  	struct group_info	*group_info;
> +	void			*security;
>  
>  	/* caches for references to the three task keyrings
>  	 * - note that key_ref_t isn't typedef'd at this point, hence the odd
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 1a15526..e5ed2ea 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -504,6 +504,18 @@ struct request_sock;
>   *	@file contains the file structure being received.
>   *	Return 0 if permission is granted.
>   *
> + * Security hooks for credential structure operations.
> + *
> + * @cred_dup:
> + *	Duplicate the credentials onto a duplicated cred structure.
> + *	@cred points to the credentials structure.  cred->security points to the
> + *	security struct that was attached to the original cred struct, but it
> + *	lacks a reference for the duplication if reference counting is needed.
> + *
> + * @cred_destroy:
> + *	Destroy the credentials attached to a cred structure.
> + *	@cred points to the credentials structure that is to be destroyed.
> + *
>   * Security hooks for task operations.
>   *
>   * @task_create:
> @@ -1257,6 +1269,9 @@ struct security_operations {
>  				    struct fown_struct * fown, int sig);
>  	int (*file_receive) (struct file * file);
>  
> +	int (*cred_dup)(struct cred *cred);
> +	void (*cred_destroy)(struct cred *cred);
> +
>  	int (*task_create) (unsigned long clone_flags);
>  	int (*task_alloc_security) (struct task_struct * p);
>  	void (*task_free_security) (struct task_struct * p);
> @@ -1864,6 +1879,16 @@ static inline int security_file_receive (struct file *file)
>  	return security_ops->file_receive (file);
>  }
>  
> +static inline int security_cred_dup(struct cred *cred)
> +{
> +	return security_ops->cred_dup(cred);
> +}
> +
> +static inline void security_cred_destroy(struct cred *cred)
> +{
> +	return security_ops->cred_destroy(cred);
> +}
> +
>  static inline int security_task_create (unsigned long clone_flags)
>  {
>  	return security_ops->task_create (clone_flags);
> @@ -2546,6 +2571,15 @@ static inline int security_file_receive (struct file *file)
>  	return 0;
>  }
>  
> +static inline int security_cred_dup(struct cred *cred)
> +{
> +	return 0;
> +}
> +
> +static inline void security_cred_destroy(struct cred *cred)
> +{
> +}
> +
>  static inline int security_task_create (unsigned long clone_flags)
>  {
>  	return 0;
> diff --git a/kernel/cred.c b/kernel/cred.c
> index e96dafe..6a9dda2 100644
> --- a/kernel/cred.c
> +++ b/kernel/cred.c
> @@ -92,6 +92,12 @@ struct cred *dup_cred(const struct cred *pcred)
>  	if (likely(cred)) {
>  		*cred = *pcred;
>  		atomic_set(&cred->usage, 1);
> +
> +		if (security_cred_dup(cred) < 0) {
> +			kfree(cred);
> +			return NULL;
> +		}
> +
>  		get_group_info(cred->group_info);
>  		key_get(key_ref_to_ptr(cred->session_keyring));
>  		key_get(key_ref_to_ptr(cred->process_keyring));
> @@ -109,6 +115,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
>  {
>  	struct cred *cred = container_of(rcu, struct cred, exterminate);
>  
> +	security_cred_destroy(cred);
>  	put_group_info(cred->group_info);
>  	key_ref_put(cred->session_keyring);
>  	key_ref_put(cred->process_keyring);
> diff --git a/security/dummy.c b/security/dummy.c
> index 62de89c..f535cc6 100644
> --- a/security/dummy.c
> +++ b/security/dummy.c
> @@ -468,6 +468,15 @@ static int dummy_file_receive (struct file *file)
>  	return 0;
>  }
>  
> +static int dummy_cred_dup(struct cred *cred)
> +{
> +	return 0;
> +}
> +
> +static void dummy_cred_destroy(struct cred *cred)
> +{
> +}
> +
>  static int dummy_task_create (unsigned long clone_flags)
>  {
>  	return 0;
> @@ -1038,6 +1047,8 @@ void security_fixup_ops (struct security_operations *ops)
>  	set_to_dummy_if_null(ops, file_set_fowner);
>  	set_to_dummy_if_null(ops, file_send_sigiotask);
>  	set_to_dummy_if_null(ops, file_receive);
> +	set_to_dummy_if_null(ops, cred_dup);
> +	set_to_dummy_if_null(ops, cred_destroy);
>  	set_to_dummy_if_null(ops, task_create);
>  	set_to_dummy_if_null(ops, task_alloc_security);
>  	set_to_dummy_if_null(ops, task_free_security);
> diff --git a/security/selinux/exports.c b/security/selinux/exports.c
> index b6f9694..29cb87a 100644
> --- a/security/selinux/exports.c
> +++ b/security/selinux/exports.c
> @@ -57,7 +57,7 @@ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
>  {
>  	if (selinux_enabled) {
>  		struct task_security_struct *tsec = tsk->security;
> -		*sid = tsec->sid;
> +		*sid = tsec->victim_sid;
>  		return;
>  	}
>  	*sid = 0;
> @@ -77,9 +77,9 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
>  int selinux_relabel_packet_permission(u32 sid)
>  {
>  	if (selinux_enabled) {
> -		struct task_security_struct *tsec = current->security;
> +		struct cred_security_struct *csec = current->cred->security;
>  
> -		return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
> +		return avc_has_perm(csec->action_sid, sid, SECCLASS_PACKET,
>  				    PACKET__RELABELTO, NULL);
>  	}
>  	return 0;
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 3694662..6fc41da 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -162,7 +162,8 @@ static int task_alloc_security(struct task_struct *task)
>  		return -ENOMEM;
>  
>  	tsec->task = task;
> -	tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
> +	tsec->osid = tsec->victim_sid = tsec->ptrace_sid =
> +		SECINITSID_UNLABELED;
>  	task->security = tsec;
>  
>  	return 0;
> @@ -177,7 +178,7 @@ static void task_free_security(struct task_struct *task)
>  
>  static int inode_alloc_security(struct inode *inode)
>  {
> -	struct task_security_struct *tsec = current->security;
> +	struct cred_security_struct *csec = current->cred->security;
>  	struct inode_security_struct *isec;
>  
>  	isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL);
> @@ -189,7 +190,7 @@ static int inode_alloc_security(struct inode *inode)
>  	isec->inode = inode;
>  	isec->sid = SECINITSID_UNLABELED;
>  	isec->sclass = SECCLASS_FILE;
> -	isec->task_sid = tsec->sid;
> +	isec->task_sid = csec->action_sid;
>  	inode->i_security = isec;
>  
>  	return 0;
> @@ -211,7 +212,7 @@ static void inode_free_security(struct inode *inode)
>  
>  static int file_alloc_security(struct file *file)
>  {
> -	struct task_security_struct *tsec = current->security;
> +	struct cred_security_struct *csec = current->cred->security;
>  	struct file_security_struct *fsec;
>  
>  	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
> @@ -219,8 +220,8 @@ static int file_alloc_security(struct file *file)
>  		return -ENOMEM;
>  
>  	fsec->file = file;
> -	fsec->sid = tsec->sid;
> -	fsec->fown_sid = tsec->sid;
> +	fsec->sid = csec->action_sid;
> +	fsec->fown_sid = csec->action_sid;
>  	file->f_security = fsec;
>  
>  	return 0;
> @@ -333,26 +334,26 @@ static match_table_t tokens = {
>  
>  static int may_context_mount_sb_relabel(u32 sid,
>  			struct superblock_security_struct *sbsec,
> -			struct task_security_struct *tsec)
> +			struct cred_security_struct *csec)
>  {
>  	int rc;
>  
> -	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> +	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
>  			  FILESYSTEM__RELABELFROM, NULL);
>  	if (rc)
>  		return rc;
>  
> -	rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
> +	rc = avc_has_perm(csec->action_sid, sid, SECCLASS_FILESYSTEM,
>  			  FILESYSTEM__RELABELTO, NULL);
>  	return rc;
>  }
>  
>  static int may_context_mount_inode_relabel(u32 sid,
>  			struct superblock_security_struct *sbsec,
> -			struct task_security_struct *tsec)
> +			struct cred_security_struct *csec)
>  {
>  	int rc;
> -	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> +	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
>  			  FILESYSTEM__RELABELFROM, NULL);
>  	if (rc)
>  		return rc;
> @@ -369,7 +370,7 @@ static int try_context_mount(struct super_block *sb, void *data)
>  	const char *name;
>  	u32 sid;
>  	int alloc = 0, rc = 0, seen = 0;
> -	struct task_security_struct *tsec = current->security;
> +	struct cred_security_struct *csec = current->cred->security;
>  	struct superblock_security_struct *sbsec = sb->s_security;
>  
>  	if (!data)
> @@ -501,7 +502,7 @@ static int try_context_mount(struct super_block *sb, void *data)
>  			goto out_free;
>  		}
>  
> -		rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> +		rc = may_context_mount_sb_relabel(sid, sbsec, csec);
>  		if (rc)
>  			goto out_free;
>  
> @@ -523,12 +524,12 @@ static int try_context_mount(struct super_block *sb, void *data)
>  		}
>  
>  		if (!fscontext) {
> -			rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> +			rc = may_context_mount_sb_relabel(sid, sbsec, csec);
>  			if (rc)
>  				goto out_free;
>  			sbsec->sid = sid;
>  		} else {
> -			rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> +			rc = may_context_mount_inode_relabel(sid, sbsec, csec);
>  			if (rc)
>  				goto out_free;
>  		}
> @@ -548,7 +549,7 @@ static int try_context_mount(struct super_block *sb, void *data)
>  			goto out_free;
>  		}
>  
> -		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> +		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
>  		if (rc)
>  			goto out_free;
>  
> @@ -568,7 +569,7 @@ static int try_context_mount(struct super_block *sb, void *data)
>  		if (sid == sbsec->def_sid)
>  			goto out_free;
>  
> -		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> +		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
>  		if (rc)
>  			goto out_free;
>  
> @@ -1023,15 +1024,22 @@ static inline u32 signal_to_av(int sig)
>  
>  /* Check permission betweeen a pair of tasks, e.g. signal checks,
>     fork check, ptrace check, etc. */
> -static int task_has_perm(struct task_struct *tsk1,
> -			 struct task_struct *tsk2,
> +static int task_has_perm(struct task_struct *actor,
> +			 struct task_struct *victim,
>  			 u32 perms)
>  {
> -	struct task_security_struct *tsec1, *tsec2;
> +	struct cred_security_struct *csec;
> +	struct task_security_struct *tsec;
> +	u32 action_sid;
> +
> +	/* the actor may not be the current task */
> +	rcu_read_lock();
> +	csec = task_cred(actor)->security;
> +	action_sid = csec->action_sid;
> +	rcu_read_unlock();
>  
> -	tsec1 = tsk1->security;
> -	tsec2 = tsk2->security;
> -	return avc_has_perm(tsec1->sid, tsec2->sid,
> +	tsec = victim->security;
> +	return avc_has_perm(action_sid, tsec->victim_sid,
>  			    SECCLASS_PROCESS, perms, NULL);
>  }
>  
> @@ -1039,16 +1047,16 @@ static int task_has_perm(struct task_struct *tsk1,
>  static int task_has_capability(struct task_struct *tsk,
>  			       int cap)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct avc_audit_data ad;
>  
> -	tsec = tsk->security;
> +	csec = tsk->cred->security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad,CAP);
>  	ad.tsk = tsk;
>  	ad.u.cap = cap;
>  
> -	return avc_has_perm(tsec->sid, tsec->sid,
> +	return avc_has_perm(csec->action_sid, csec->action_sid,
>  			    SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
>  }
>  
> @@ -1056,11 +1064,11 @@ static int task_has_capability(struct task_struct *tsk,
>  static int task_has_system(struct task_struct *tsk,
>  			   u32 perms)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  
> -	tsec = tsk->security;
> +	csec = tsk->cred->security;
>  
> -	return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
> +	return avc_has_perm(csec->action_sid, SECINITSID_KERNEL,
>  			    SECCLASS_SYSTEM, perms, NULL);
>  }
>  
> @@ -1072,14 +1080,14 @@ static int inode_has_perm(struct task_struct *tsk,
>  			  u32 perms,
>  			  struct avc_audit_data *adp)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct inode_security_struct *isec;
>  	struct avc_audit_data ad;
>  
>  	if (unlikely (IS_PRIVATE (inode)))
>  		return 0;
>  
> -	tsec = tsk->security;
> +	csec = tsk->cred->security;
>  	isec = inode->i_security;
>  
>  	if (!adp) {
> @@ -1088,7 +1096,8 @@ static int inode_has_perm(struct task_struct *tsk,
>  		ad.u.fs.inode = inode;
>  	}
>  
> -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
> +	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> +			    adp);
>  }
>  
>  /* Same as inode_has_perm, but pass explicit audit data containing
> @@ -1119,7 +1128,7 @@ static int file_has_perm(struct task_struct *tsk,
>  				struct file *file,
>  				u32 av)
>  {
> -	struct task_security_struct *tsec = tsk->security;
> +	struct cred_security_struct *csec = tsk->cred->security;
>  	struct file_security_struct *fsec = file->f_security;
>  	struct vfsmount *mnt = file->f_path.mnt;
>  	struct dentry *dentry = file->f_path.dentry;
> @@ -1131,8 +1140,8 @@ static int file_has_perm(struct task_struct *tsk,
>  	ad.u.fs.mnt = mnt;
>  	ad.u.fs.dentry = dentry;
>  
> -	if (tsec->sid != fsec->sid) {
> -		rc = avc_has_perm(tsec->sid, fsec->sid,
> +	if (csec->action_sid != fsec->sid) {
> +		rc = avc_has_perm(csec->action_sid, fsec->sid,
>  				  SECCLASS_FD,
>  				  FD__USE,
>  				  &ad);
> @@ -1152,36 +1161,36 @@ static int may_create(struct inode *dir,
>  		      struct dentry *dentry,
>  		      u16 tclass)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct inode_security_struct *dsec;
>  	struct superblock_security_struct *sbsec;
>  	u32 newsid;
>  	struct avc_audit_data ad;
>  	int rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	dsec = dir->i_security;
>  	sbsec = dir->i_sb->s_security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, FS);
>  	ad.u.fs.dentry = dentry;
>  
> -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
> +	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR,
>  			  DIR__ADD_NAME | DIR__SEARCH,
>  			  &ad);
>  	if (rc)
>  		return rc;
>  
> -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> -		newsid = tsec->create_sid;
> +	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> +		newsid = csec->create_sid;
>  	} else {
> -		rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
> -					     &newsid);
> +		rc = security_transition_sid(csec->action_sid, dsec->sid,
> +					     tclass, &newsid);
>  		if (rc)
>  			return rc;
>  	}
>  
> -	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
> +	rc = avc_has_perm(csec->action_sid, newsid, tclass, FILE__CREATE, &ad);
>  	if (rc)
>  		return rc;
>  
> @@ -1194,11 +1203,12 @@ static int may_create(struct inode *dir,
>  static int may_create_key(u32 ksid,
>  			  struct task_struct *ctx)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  
> -	tsec = ctx->security;
> +	csec = ctx->cred->security;
>  
> -	return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
> +	return avc_has_perm(csec->action_sid, ksid, SECCLASS_KEY, KEY__CREATE,
> +			    NULL);
>  }
>  
>  #define MAY_LINK   0
> @@ -1211,13 +1221,13 @@ static int may_link(struct inode *dir,
>  		    int kind)
>  
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct inode_security_struct *dsec, *isec;
>  	struct avc_audit_data ad;
>  	u32 av;
>  	int rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	dsec = dir->i_security;
>  	isec = dentry->d_inode->i_security;
>  
> @@ -1226,7 +1236,7 @@ static int may_link(struct inode *dir,
>  
>  	av = DIR__SEARCH;
>  	av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
> -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
> +	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR, av, &ad);
>  	if (rc)
>  		return rc;
>  
> @@ -1245,7 +1255,7 @@ static int may_link(struct inode *dir,
>  		return 0;
>  	}
>  
> -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
> +	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, av, &ad);
>  	return rc;
>  }
>  
> @@ -1254,14 +1264,14 @@ static inline int may_rename(struct inode *old_dir,
>  			     struct inode *new_dir,
>  			     struct dentry *new_dentry)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
>  	struct avc_audit_data ad;
>  	u32 av;
>  	int old_is_dir, new_is_dir;
>  	int rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	old_dsec = old_dir->i_security;
>  	old_isec = old_dentry->d_inode->i_security;
>  	old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
> @@ -1270,16 +1280,16 @@ static inline int may_rename(struct inode *old_dir,
>  	AVC_AUDIT_DATA_INIT(&ad, FS);
>  
>  	ad.u.fs.dentry = old_dentry;
> -	rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
> +	rc = avc_has_perm(csec->action_sid, old_dsec->sid, SECCLASS_DIR,
>  			  DIR__REMOVE_NAME | DIR__SEARCH, &ad);
>  	if (rc)
>  		return rc;
> -	rc = avc_has_perm(tsec->sid, old_isec->sid,
> +	rc = avc_has_perm(csec->action_sid, old_isec->sid,
>  			  old_isec->sclass, FILE__RENAME, &ad);
>  	if (rc)
>  		return rc;
>  	if (old_is_dir && new_dir != old_dir) {
> -		rc = avc_has_perm(tsec->sid, old_isec->sid,
> +		rc = avc_has_perm(csec->action_sid, old_isec->sid,
>  				  old_isec->sclass, DIR__REPARENT, &ad);
>  		if (rc)
>  			return rc;
> @@ -1289,15 +1299,17 @@ static inline int may_rename(struct inode *old_dir,
>  	av = DIR__ADD_NAME | DIR__SEARCH;
>  	if (new_dentry->d_inode)
>  		av |= DIR__REMOVE_NAME;
> -	rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
> +	rc = avc_has_perm(csec->action_sid, new_dsec->sid, SECCLASS_DIR, av,
> +			  &ad);
>  	if (rc)
>  		return rc;
>  	if (new_dentry->d_inode) {
>  		new_isec = new_dentry->d_inode->i_security;
>  		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
> -		rc = avc_has_perm(tsec->sid, new_isec->sid,
> +		rc = avc_has_perm(csec->action_sid, new_isec->sid,
>  				  new_isec->sclass,
> -				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
> +				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
> +				  &ad);
>  		if (rc)
>  			return rc;
>  	}
> @@ -1311,12 +1323,12 @@ static int superblock_has_perm(struct task_struct *tsk,
>  			       u32 perms,
>  			       struct avc_audit_data *ad)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct superblock_security_struct *sbsec;
>  
> -	tsec = tsk->security;
> +	csec = tsk->cred->security;
>  	sbsec = sb->s_security;
> -	return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> +	return avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
>  			    perms, ad);
>  }
>  
> @@ -1369,7 +1381,7 @@ static inline u32 file_to_av(struct file *file)
>  
>  static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
>  {
> -	struct task_security_struct *psec = parent->security;
> +	struct cred_security_struct *psec;
>  	struct task_security_struct *csec = child->security;
>  	int rc;
>  
> @@ -1379,8 +1391,12 @@ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
>  
>  	rc = task_has_perm(parent, child, PROCESS__PTRACE);
>  	/* Save the SID of the tracing process for later use in apply_creds. */
> -	if (!(child->ptrace & PT_PTRACED) && !rc)
> -		csec->ptrace_sid = psec->sid;
> +	if (!(child->ptrace & PT_PTRACED) && !rc) {
> +		rcu_read_lock();
> +		psec = task_cred(parent)->security;
> +		csec->ptrace_sid = psec->action_sid;
> +		rcu_read_unlock();
> +	}
>  	return rc;
>  }
>  
> @@ -1470,7 +1486,7 @@ static int selinux_sysctl(ctl_table *table, int op)
>  {
>  	int error = 0;
>  	u32 av;
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	u32 tsid;
>  	int rc;
>  
> @@ -1478,7 +1494,7 @@ static int selinux_sysctl(ctl_table *table, int op)
>  	if (rc)
>  		return rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  
>  	rc = selinux_sysctl_get_sid(table, (op == 0001) ?
>  				    SECCLASS_DIR : SECCLASS_FILE, &tsid);
> @@ -1490,7 +1506,7 @@ static int selinux_sysctl(ctl_table *table, int op)
>  	/* The op values are "defined" in sysctl.c, thereby creating
>  	 * a bad coupling between this module and sysctl.c */
>  	if(op == 001) {
> -		error = avc_has_perm(tsec->sid, tsid,
> +		error = avc_has_perm(csec->action_sid, tsid,
>  				     SECCLASS_DIR, DIR__SEARCH, NULL);
>  	} else {
>  		av = 0;
> @@ -1499,7 +1515,7 @@ static int selinux_sysctl(ctl_table *table, int op)
>  		if (op & 002)
>  			av |= FILE__WRITE;
>  		if (av)
> -			error = avc_has_perm(tsec->sid, tsid,
> +			error = avc_has_perm(csec->action_sid, tsid,
>  					     SECCLASS_FILE, av, NULL);
>          }
>  
> @@ -1587,11 +1603,11 @@ static int selinux_syslog(int type)
>  static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
>  {
>  	int rc, cap_sys_admin = 0;
> -	struct task_security_struct *tsec = current->security;
> +	struct cred_security_struct *csec = current->cred->security;
>  
>  	rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
>  	if (rc == 0)
> -		rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
> +		rc = avc_has_perm_noaudit(csec->action_sid, csec->action_sid,
>  					  SECCLASS_CAPABILITY,
>  					  CAP_TO_MASK(CAP_SYS_ADMIN),
>  					  0,
> @@ -1624,6 +1640,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
>  static int selinux_bprm_set_security(struct linux_binprm *bprm)
>  {
>  	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct inode *inode = bprm->file->f_path.dentry->d_inode;
>  	struct inode_security_struct *isec;
>  	struct bprm_security_struct *bsec;
> @@ -1641,15 +1658,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
>  		return 0;
>  
>  	tsec = current->security;
> +	csec = bprm->cred->security;
>  	isec = inode->i_security;
>  
>  	/* Default to the current task SID. */
> -	bsec->sid = tsec->sid;
> +	bsec->sid = csec->action_sid;
>  
>  	/* Reset fs, key, and sock SIDs on execve. */
> -	tsec->create_sid = 0;
> -	tsec->keycreate_sid = 0;
> -	tsec->sockcreate_sid = 0;
> +	csec->create_sid = 0;
> +	csec->keycreate_sid = 0;
> +	csec->sockcreate_sid = 0;
>  
>  	if (tsec->exec_sid) {
>  		newsid = tsec->exec_sid;
> @@ -1657,7 +1675,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
>  		tsec->exec_sid = 0;
>  	} else {
>  		/* Check for a default transition on this program. */
> -		rc = security_transition_sid(tsec->sid, isec->sid,
> +		rc = security_transition_sid(csec->action_sid, isec->sid,
>  		                             SECCLASS_PROCESS, &newsid);
>  		if (rc)
>  			return rc;
> @@ -1668,16 +1686,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
>  	ad.u.fs.dentry = bprm->file->f_path.dentry;
>  
>  	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
> -		newsid = tsec->sid;
> +		newsid = csec->action_sid;
>  
> -        if (tsec->sid == newsid) {
> -		rc = avc_has_perm(tsec->sid, isec->sid,
> +        if (csec->action_sid == newsid) {
> +		rc = avc_has_perm(csec->action_sid, isec->sid,
>  				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
>  		if (rc)
>  			return rc;
>  	} else {
>  		/* Check permissions for the transition. */
> -		rc = avc_has_perm(tsec->sid, newsid,
> +		rc = avc_has_perm(csec->action_sid, newsid,
>  				  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
>  		if (rc)
>  			return rc;
> @@ -1709,11 +1727,11 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm)
>  	struct task_security_struct *tsec = current->security;
>  	int atsecure = 0;
>  
> -	if (tsec->osid != tsec->sid) {
> +	if (tsec->osid != tsec->victim_sid) {
>  		/* Enable secure mode for SIDs transitions unless
>  		   the noatsecure permission is granted between
>  		   the two SIDs, i.e. ahp returns 0. */
> -		atsecure = avc_has_perm(tsec->osid, tsec->sid,
> +		atsecure = avc_has_perm(tsec->osid, tsec->victim_sid,
>  					 SECCLASS_PROCESS,
>  					 PROCESS__NOATSECURE, NULL);
>  	}
> @@ -1823,6 +1841,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
>  static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
>  {
>  	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct bprm_security_struct *bsec;
>  	u32 sid;
>  	int rc;
> @@ -1830,17 +1849,17 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
>  	secondary_ops->bprm_apply_creds(bprm, unsafe);
>  
>  	tsec = current->security;
> -
> +	csec = bprm->cred->security;
>  	bsec = bprm->security;
>  	sid = bsec->sid;
>  
> -	tsec->osid = tsec->sid;
> +	tsec->osid = tsec->victim_sid;
>  	bsec->unsafe = 0;
> -	if (tsec->sid != sid) {
> +	if (tsec->victim_sid != sid) {
>  		/* Check for shared state.  If not ok, leave SID
>  		   unchanged and kill. */
>  		if (unsafe & LSM_UNSAFE_SHARE) {
> -			rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
> +			rc = avc_has_perm(tsec->victim_sid, sid, SECCLASS_PROCESS,
>  					PROCESS__SHARE, NULL);
>  			if (rc) {
>  				bsec->unsafe = 1;
> @@ -1859,7 +1878,9 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
>  				return;
>  			}
>  		}
> -		tsec->sid = sid;
> +		if (csec->action_sid == tsec->victim_sid)
> +			csec->action_sid = sid;
> +		tsec->victim_sid = sid;
>  	}
>  }
>  
> @@ -1881,7 +1902,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
>  		force_sig_specific(SIGKILL, current);
>  		return;
>  	}
> -	if (tsec->osid == tsec->sid)
> +	if (tsec->osid == tsec->victim_sid)
>  		return;
>  
>  	/* Close files for which the new task SID is not authorized. */
> @@ -1893,7 +1914,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
>  	   signals. This must occur _after_ the task SID has
>  	  been updated so that any kill done after the flush
>  	  will be checked against the new SID. */
> -	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
> +	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
>  			  PROCESS__SIGINH, NULL);
>  	if (rc) {
>  		memset(&itimer, 0, sizeof itimer);
> @@ -1920,7 +1941,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
>  	   than the default soft limit for cases where the default
>  	   is lower than the hard limit, e.g. RLIMIT_CORE or
>  	   RLIMIT_STACK.*/
> -	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
> +	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
>  			  PROCESS__RLIMITINH, NULL);
>  	if (rc) {
>  		for (i = 0; i < RLIM_NLIMITS; i++) {
> @@ -2122,21 +2143,21 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
>  				       char **name, void **value,
>  				       size_t *len)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct inode_security_struct *dsec;
>  	struct superblock_security_struct *sbsec;
>  	u32 newsid, clen;
>  	int rc;
>  	char *namep = NULL, *context;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	dsec = dir->i_security;
>  	sbsec = dir->i_sb->s_security;
>  
> -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> -		newsid = tsec->create_sid;
> +	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> +		newsid = csec->create_sid;
>  	} else {
> -		rc = security_transition_sid(tsec->sid, dsec->sid,
> +		rc = security_transition_sid(csec->action_sid, dsec->sid,
>  					     inode_mode_to_security_class(inode->i_mode),
>  					     &newsid);
>  		if (rc) {
> @@ -2295,7 +2316,7 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
>  
>  static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
>  {
> -	struct task_security_struct *tsec = current->security;
> +	struct cred_security_struct *csec = current->cred->security;
>  	struct inode *inode = dentry->d_inode;
>  	struct inode_security_struct *isec = inode->i_security;
>  	struct superblock_security_struct *sbsec;
> @@ -2327,7 +2348,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
>  	AVC_AUDIT_DATA_INIT(&ad,FS);
>  	ad.u.fs.dentry = dentry;
>  
> -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
> +	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass,
>  			  FILE__RELABELFROM, &ad);
>  	if (rc)
>  		return rc;
> @@ -2336,12 +2357,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
>  	if (rc)
>  		return rc;
>  
> -	rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
> +	rc = avc_has_perm(csec->action_sid, newsid, isec->sclass,
>  			  FILE__RELABELTO, &ad);
>  	if (rc)
>  		return rc;
>  
> -	rc = security_validate_transition(isec->sid, newsid, tsec->sid,
> +	rc = security_validate_transition(isec->sid, newsid, csec->action_sid,
>  	                                  isec->sclass);
>  	if (rc)
>  		return rc;
> @@ -2575,8 +2596,9 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
>  			     unsigned long prot, unsigned long flags,
>  			     unsigned long addr, unsigned long addr_only)
>  {
> +	struct cred_security_struct *csec = current->cred->security;
>  	int rc = 0;
> -	u32 sid = ((struct task_security_struct*)(current->security))->sid;
> +	u32 sid = csec->action_sid;
>  
>  	if (addr < mmap_min_addr)
>  		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
> @@ -2690,7 +2712,7 @@ static int selinux_file_set_fowner(struct file *file)
>  
>  	tsec = current->security;
>  	fsec = file->f_security;
> -	fsec->fown_sid = tsec->sid;
> +	fsec->fown_sid = tsec->victim_sid;
>  
>  	return 0;
>  }
> @@ -2714,7 +2736,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
>  	else
>  		perm = signal_to_av(signum);
>  
> -	return avc_has_perm(fsec->fown_sid, tsec->sid,
> +	return avc_has_perm(fsec->fown_sid, tsec->victim_sid,
>  			    SECCLASS_PROCESS, perm, NULL);
>  }
>  
> @@ -2723,6 +2745,31 @@ static int selinux_file_receive(struct file *file)
>  	return file_has_perm(current, file, file_to_av(file));
>  }
>  
> +/* credential security operations */
> +
> +/*
> + * duplicate the security information attached to a credentials record that is
> + * itself undergoing duplication
> + */
> +static int selinux_cred_dup(struct cred *cred)
> +{
> +	cred->security = kmemdup(cred->security,
> +				 sizeof(struct cred_security_struct),
> +				 GFP_KERNEL);
> +	return cred->security ? 0 : -ENOMEM;
> +}
> +
> +/*
> + * destroy the security information attached to a credentials record
> + * - this is done under RCU, and may not be associated with the task that set it
> + *   up
> + */
> +static void selinux_cred_destroy(struct cred *cred)
> +{
> +	kfree(cred->security);
> +}
> +
> +
>  /* task security operations */
>  
>  static int selinux_task_create(unsigned long clone_flags)
> @@ -2749,13 +2796,10 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
>  	tsec2 = tsk->security;
>  
>  	tsec2->osid = tsec1->osid;
> -	tsec2->sid = tsec1->sid;
> +	tsec2->victim_sid = tsec1->victim_sid;
>  
> -	/* Retain the exec, fs, key, and sock SIDs across fork */
> +	/* Retain the exec SID across fork */
>  	tsec2->exec_sid = tsec1->exec_sid;
> -	tsec2->create_sid = tsec1->create_sid;
> -	tsec2->keycreate_sid = tsec1->keycreate_sid;
> -	tsec2->sockcreate_sid = tsec1->sockcreate_sid;
>  
>  	/* Retain ptracer SID across fork, if any.
>  	   This will be reset by the ptrace hook upon any
> @@ -2893,7 +2937,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
>  		perm = signal_to_av(sig);
>  	tsec = p->security;
>  	if (secid)
> -		rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
> +		rc = avc_has_perm(secid, tsec->victim_sid,
> +				  SECCLASS_PROCESS, perm, NULL);
>  	else
>  		rc = task_has_perm(current, p, perm);
>  	return rc;
> @@ -2927,8 +2972,8 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
>  	secondary_ops->task_reparent_to_init(p);
>  
>  	tsec = p->security;
> -	tsec->osid = tsec->sid;
> -	tsec->sid = SECINITSID_KERNEL;
> +	tsec->osid = tsec->victim_sid;
> +	tsec->victim_sid = SECINITSID_KERNEL;
>  	return;
>  }
>  
> @@ -2938,7 +2983,7 @@ static void selinux_task_to_inode(struct task_struct *p,
>  	struct task_security_struct *tsec = p->security;
>  	struct inode_security_struct *isec = inode->i_security;
>  
> -	isec->sid = tsec->sid;
> +	isec->sid = tsec->victim_sid;
>  	isec->initialized = 1;
>  	return;
>  }
> @@ -3163,11 +3208,11 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
>  			   u32 perms)
>  {
>  	struct inode_security_struct *isec;
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct avc_audit_data ad;
>  	int err = 0;
>  
> -	tsec = task->security;
> +	csec = task->cred->security;
>  	isec = SOCK_INODE(sock)->i_security;
>  
>  	if (isec->sid == SECINITSID_KERNEL)
> @@ -3175,7 +3220,8 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
>  
>  	AVC_AUDIT_DATA_INIT(&ad,NET);
>  	ad.u.net.sk = sock->sk;
> -	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
> +	err = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> +			   &ad);
>  
>  out:
>  	return err;
> @@ -3185,15 +3231,15 @@ static int selinux_socket_create(int family, int type,
>  				 int protocol, int kern)
>  {
>  	int err = 0;
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	u32 newsid;
>  
>  	if (kern)
>  		goto out;
>  
> -	tsec = current->security;
> -	newsid = tsec->sockcreate_sid ? : tsec->sid;
> -	err = avc_has_perm(tsec->sid, newsid,
> +	csec = current->cred->security;
> +	newsid = csec->sockcreate_sid ? : csec->action_sid;
> +	err = avc_has_perm(csec->action_sid, newsid,
>  			   socket_type_to_security_class(family, type,
>  			   protocol), SOCKET__CREATE, NULL);
>  
> @@ -3206,14 +3252,14 @@ static int selinux_socket_post_create(struct socket *sock, int family,
>  {
>  	int err = 0;
>  	struct inode_security_struct *isec;
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct sk_security_struct *sksec;
>  	u32 newsid;
>  
>  	isec = SOCK_INODE(sock)->i_security;
>  
> -	tsec = current->security;
> -	newsid = tsec->sockcreate_sid ? : tsec->sid;
> +	csec = current->cred->security;
> +	newsid = csec->sockcreate_sid ? : csec->action_sid;
>  	isec->sclass = socket_type_to_security_class(family, type, protocol);
>  	isec->sid = kern ? SECINITSID_KERNEL : newsid;
>  	isec->initialized = 1;
> @@ -4027,7 +4073,7 @@ static int ipc_alloc_security(struct task_struct *task,
>  			      struct kern_ipc_perm *perm,
>  			      u16 sclass)
>  {
> -	struct task_security_struct *tsec = task->security;
> +	struct cred_security_struct *csec = task->cred->security;
>  	struct ipc_security_struct *isec;
>  
>  	isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
> @@ -4036,7 +4082,7 @@ static int ipc_alloc_security(struct task_struct *task,
>  
>  	isec->sclass = sclass;
>  	isec->ipc_perm = perm;
> -	isec->sid = tsec->sid;
> +	isec->sid = csec->action_sid;
>  	perm->security = isec;
>  
>  	return 0;
> @@ -4075,17 +4121,18 @@ static void msg_msg_free_security(struct msg_msg *msg)
>  static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
>  			u32 perms)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = ipc_perms->security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>  	ad.u.ipc_id = ipc_perms->key;
>  
> -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
> +	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> +			    &ad);
>  }
>  
>  static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
> @@ -4101,7 +4148,7 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
>  /* message queue security operations */
>  static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  	int rc;
> @@ -4110,13 +4157,13 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
>  	if (rc)
>  		return rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = msq->q_perm.security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>   	ad.u.ipc_id = msq->q_perm.key;
>  
> -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
>  			  MSGQ__CREATE, &ad);
>  	if (rc) {
>  		ipc_free_security(&msq->q_perm);
> @@ -4132,17 +4179,17 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
>  
>  static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = msq->q_perm.security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>  	ad.u.ipc_id = msq->q_perm.key;
>  
> -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
>  			    MSGQ__ASSOCIATE, &ad);
>  }
>  
> @@ -4176,13 +4223,13 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
>  
>  static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct msg_security_struct *msec;
>  	struct avc_audit_data ad;
>  	int rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = msq->q_perm.security;
>  	msec = msg->security;
>  
> @@ -4194,7 +4241,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
>  		 * Compute new sid based on current process and
>  		 * message queue this message will be stored in
>  		 */
> -		rc = security_transition_sid(tsec->sid,
> +		rc = security_transition_sid(csec->action_sid,
>  					     isec->sid,
>  					     SECCLASS_MSG,
>  					     &msec->sid);
> @@ -4206,11 +4253,11 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
>  	ad.u.ipc_id = msq->q_perm.key;
>  
>  	/* Can this process write to the queue? */
> -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
>  			  MSGQ__WRITE, &ad);
>  	if (!rc)
>  		/* Can this process send the message */
> -		rc = avc_has_perm(tsec->sid, msec->sid,
> +		rc = avc_has_perm(csec->action_sid, msec->sid,
>  				  SECCLASS_MSG, MSG__SEND, &ad);
>  	if (!rc)
>  		/* Can the message be put in the queue? */
> @@ -4237,10 +4284,10 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>   	ad.u.ipc_id = msq->q_perm.key;
>  
> -	rc = avc_has_perm(tsec->sid, isec->sid,
> +	rc = avc_has_perm(tsec->victim_sid, isec->sid,
>  			  SECCLASS_MSGQ, MSGQ__READ, &ad);
>  	if (!rc)
> -		rc = avc_has_perm(tsec->sid, msec->sid,
> +		rc = avc_has_perm(tsec->victim_sid, msec->sid,
>  				  SECCLASS_MSG, MSG__RECEIVE, &ad);
>  	return rc;
>  }
> @@ -4248,7 +4295,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
>  /* Shared Memory security operations */
>  static int selinux_shm_alloc_security(struct shmid_kernel *shp)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  	int rc;
> @@ -4257,13 +4304,13 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
>  	if (rc)
>  		return rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = shp->shm_perm.security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>   	ad.u.ipc_id = shp->shm_perm.key;
>  
> -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
> +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
>  			  SHM__CREATE, &ad);
>  	if (rc) {
>  		ipc_free_security(&shp->shm_perm);
> @@ -4279,17 +4326,17 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
>  
>  static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = shp->shm_perm.security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>  	ad.u.ipc_id = shp->shm_perm.key;
>  
> -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
> +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
>  			    SHM__ASSOCIATE, &ad);
>  }
>  
> @@ -4347,7 +4394,7 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
>  /* Semaphore security operations */
>  static int selinux_sem_alloc_security(struct sem_array *sma)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  	int rc;
> @@ -4356,13 +4403,13 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
>  	if (rc)
>  		return rc;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = sma->sem_perm.security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>   	ad.u.ipc_id = sma->sem_perm.key;
>  
> -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
> +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
>  			  SEM__CREATE, &ad);
>  	if (rc) {
>  		ipc_free_security(&sma->sem_perm);
> @@ -4378,17 +4425,17 @@ static void selinux_sem_free_security(struct sem_array *sma)
>  
>  static int selinux_sem_associate(struct sem_array *sma, int semflg)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct ipc_security_struct *isec;
>  	struct avc_audit_data ad;
>  
> -	tsec = current->security;
> +	csec = current->cred->security;
>  	isec = sma->sem_perm.security;
>  
>  	AVC_AUDIT_DATA_INIT(&ad, IPC);
>  	ad.u.ipc_id = sma->sem_perm.key;
>  
> -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
> +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
>  			    SEM__ASSOCIATE, &ad);
>  }
>  
> @@ -4504,6 +4551,7 @@ static int selinux_getprocattr(struct task_struct *p,
>  			       char *name, char **value)
>  {
>  	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	u32 sid;
>  	int error;
>  	unsigned len;
> @@ -4515,22 +4563,25 @@ static int selinux_getprocattr(struct task_struct *p,
>  	}
>  
>  	tsec = p->security;
> +	rcu_read_lock();
> +	csec = task_cred(p)->security;
>  
>  	if (!strcmp(name, "current"))
> -		sid = tsec->sid;
> +		sid = tsec->victim_sid;
>  	else if (!strcmp(name, "prev"))
>  		sid = tsec->osid;
>  	else if (!strcmp(name, "exec"))
>  		sid = tsec->exec_sid;
>  	else if (!strcmp(name, "fscreate"))
> -		sid = tsec->create_sid;
> +		sid = csec->create_sid;
>  	else if (!strcmp(name, "keycreate"))
> -		sid = tsec->keycreate_sid;
> +		sid = csec->keycreate_sid;
>  	else if (!strcmp(name, "sockcreate"))
> -		sid = tsec->sockcreate_sid;
> +		sid = csec->sockcreate_sid;
>  	else
> -		return -EINVAL;
> +		goto invalid;
>  
> +	rcu_read_unlock();
>  	if (!sid)
>  		return 0;
>  
> @@ -4538,13 +4589,20 @@ static int selinux_getprocattr(struct task_struct *p,
>  	if (error)
>  		return error;
>  	return len;
> +
> +invalid:
> +	rcu_read_unlock();
> +	return -EINVAL;
>  }
>  
>  static int selinux_setprocattr(struct task_struct *p,
>  			       char *name, void *value, size_t size)
>  {
>  	struct task_security_struct *tsec;
> -	u32 sid = 0;
> +	struct cred_security_struct *csec;
> +	struct av_decision avd;
> +	struct cred *cred;
> +	u32 sid = 0, perm;
>  	int error;
>  	char *str = value;
>  
> @@ -4560,17 +4618,19 @@ static int selinux_setprocattr(struct task_struct *p,
>  	 * above restriction is ever removed.
>  	 */
>  	if (!strcmp(name, "exec"))
> -		error = task_has_perm(current, p, PROCESS__SETEXEC);
> +		perm = PROCESS__SETEXEC;
>  	else if (!strcmp(name, "fscreate"))
> -		error = task_has_perm(current, p, PROCESS__SETFSCREATE);
> +		perm = PROCESS__SETFSCREATE;
>  	else if (!strcmp(name, "keycreate"))
> -		error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
> +		perm = PROCESS__SETKEYCREATE;
>  	else if (!strcmp(name, "sockcreate"))
> -		error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
> +		perm = PROCESS__SETSOCKCREATE;
>  	else if (!strcmp(name, "current"))
> -		error = task_has_perm(current, p, PROCESS__SETCURRENT);
> +		perm = PROCESS__SETCURRENT;
>  	else
> -		error = -EINVAL;
> +		return -EINVAL;
> +
> +	error = task_has_perm(current, p, perm);
>  	if (error)
>  		return error;
>  
> @@ -4592,20 +4652,37 @@ static int selinux_setprocattr(struct task_struct *p,
>  	   checks and may_create for the file creation checks. The
>  	   operation will then fail if the context is not permitted. */
>  	tsec = p->security;
> -	if (!strcmp(name, "exec"))
> +	csec = p->cred->security;
> +	switch (perm) {
> +	case PROCESS__SETEXEC:
>  		tsec->exec_sid = sid;
> -	else if (!strcmp(name, "fscreate"))
> -		tsec->create_sid = sid;
> -	else if (!strcmp(name, "keycreate")) {
> +		break;
> +
> +	case PROCESS__SETKEYCREATE:
>  		error = may_create_key(sid, p);
>  		if (error)
>  			return error;
> -		tsec->keycreate_sid = sid;
> -	} else if (!strcmp(name, "sockcreate"))
> -		tsec->sockcreate_sid = sid;
> -	else if (!strcmp(name, "current")) {
> -		struct av_decision avd;
> +	case PROCESS__SETFSCREATE:
> +	case PROCESS__SETSOCKCREATE:
> +		cred = dup_cred(current->cred);
> +		if (!cred)
> +			return -ENOMEM;
> +		csec = cred->security;
> +		switch (perm) {
> +		case PROCESS__SETKEYCREATE:
> +			csec->keycreate_sid = sid;
> +			break;
> +		case PROCESS__SETFSCREATE:
> +			csec->create_sid = sid;
> +			break;
> +		case PROCESS__SETSOCKCREATE:
> +			csec->sockcreate_sid = sid;
> +			break;
> +		}
> +		set_current_cred(cred);
> +		break;
>  
> +	case PROCESS__SETCURRENT:
>  		if (sid == 0)
>  			return -EINVAL;
>  
> @@ -4624,11 +4701,16 @@ static int selinux_setprocattr(struct task_struct *p,
>                  }
>  
>  		/* Check permissions for the transition. */
> -		error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
> +		error = avc_has_perm(csec->action_sid, sid, SECCLASS_PROCESS,
>  		                     PROCESS__DYNTRANSITION, NULL);
>  		if (error)
>  			return error;
>  
> +		cred = dup_cred(current->cred);
> +		if (!cred)
> +			return -ENOMEM;
> +		csec = cred->security;
> +
>  		/* Check for ptracing, and update the task SID if ok.
>  		   Otherwise, leave SID unchanged and fail. */
>  		task_lock(p);
> @@ -4636,20 +4718,25 @@ static int selinux_setprocattr(struct task_struct *p,
>  			error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
>  						     SECCLASS_PROCESS,
>  						     PROCESS__PTRACE, 0, &avd);
> -			if (!error)
> -				tsec->sid = sid;
> +			if (!error) {
> +				csec->action_sid = tsec->victim_sid = sid;
> +			}
>  			task_unlock(p);
>  			avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
>  				  PROCESS__PTRACE, &avd, error, NULL);
> -			if (error)
> +			if (error) {
> +				put_cred(cred);
>  				return error;
> +			}
>  		} else {
> -			tsec->sid = sid;
> +			csec->action_sid = tsec->victim_sid = sid;
>  			task_unlock(p);
>  		}
> -	}
> -	else
> +		set_current_cred(cred);
> +		break;
> +	default:
>  		return -EINVAL;
> +	}
>  
>  	return size;
>  }
> @@ -4669,18 +4756,21 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
>  static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
>  			     unsigned long flags)
>  {
> -	struct task_security_struct *tsec = tsk->security;
> +	struct cred_security_struct *csec;
>  	struct key_security_struct *ksec;
>  
>  	ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
>  	if (!ksec)
>  		return -ENOMEM;
>  
> +	rcu_read_lock();
> +	csec = task_cred(tsk)->security;
>  	ksec->obj = k;
> -	if (tsec->keycreate_sid)
> -		ksec->sid = tsec->keycreate_sid;
> +	if (csec->keycreate_sid)
> +		ksec->sid = csec->keycreate_sid;
>  	else
> -		ksec->sid = tsec->sid;
> +		ksec->sid = csec->action_sid;
> +	rcu_read_unlock();
>  	k->security = ksec;
>  
>  	return 0;
> @@ -4695,17 +4785,13 @@ static void selinux_key_free(struct key *k)
>  }
>  
>  static int selinux_key_permission(key_ref_t key_ref,
> -			    struct task_struct *ctx,
> -			    key_perm_t perm)
> +				  struct task_struct *ctx,
> +				  key_perm_t perm)
>  {
>  	struct key *key;
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  	struct key_security_struct *ksec;
> -
> -	key = key_ref_to_ptr(key_ref);
> -
> -	tsec = ctx->security;
> -	ksec = key->security;
> +	u32 action_sid;
>  
>  	/* if no specific permissions are requested, we skip the
>  	   permission check. No serious, additional covert channels
> @@ -4713,7 +4799,16 @@ static int selinux_key_permission(key_ref_t key_ref,
>  	if (perm == 0)
>  		return 0;
>  
> -	return avc_has_perm(tsec->sid, ksec->sid,
> +	key = key_ref_to_ptr(key_ref);
> +
> +	rcu_read_lock();
> +	csec = task_cred(ctx)->security;
> +	action_sid = csec->action_sid;
> +	rcu_read_unlock();
> +
> +	ksec = key->security;
> +
> +	return avc_has_perm(action_sid, ksec->sid,
>  			    SECCLASS_KEY, perm, NULL);
>  }
>  
> @@ -4788,6 +4883,9 @@ static struct security_operations selinux_ops = {
>  	.file_send_sigiotask =		selinux_file_send_sigiotask,
>  	.file_receive =			selinux_file_receive,
>  
> +	.cred_dup =			selinux_cred_dup,
> +	.cred_destroy =			selinux_cred_destroy,
> +
>  	.task_create =			selinux_task_create,
>  	.task_alloc_security =		selinux_task_alloc_security,
>  	.task_free_security =		selinux_task_free_security,
> @@ -4896,6 +4994,17 @@ static struct security_operations selinux_ops = {
>  #endif
>  };
>  
> +/*
> + * initial security credentials
> + * - attached to init_cred which is never released
> + */
> +static struct cred_security_struct init_cred_sec = {
> +	.action_sid	= SECINITSID_KERNEL,
> +	.create_sid	= SECINITSID_UNLABELED,
> +	.keycreate_sid	= SECINITSID_UNLABELED,
> +	.sockcreate_sid	= SECINITSID_UNLABELED,
> +};
> +
>  static __init int selinux_init(void)
>  {
>  	struct task_security_struct *tsec;
> @@ -4907,11 +5016,15 @@ static __init int selinux_init(void)
>  
>  	printk(KERN_INFO "SELinux:  Initializing.\n");
>  
> +	/* Set the security state for the initial credentials */
> +	init_cred.security = &init_cred_sec;
> +	BUG_ON(current->cred != &init_cred);
> +
>  	/* Set the security state for the initial task. */
>  	if (task_alloc_security(current))
>  		panic("SELinux:  Failed to initialize initial task.\n");
>  	tsec = current->security;
> -	tsec->osid = tsec->sid = SECINITSID_KERNEL;
> +	tsec->osid = tsec->victim_sid = SECINITSID_KERNEL;
>  
>  	sel_inode_cache = kmem_cache_create("selinux_inode_security",
>  					    sizeof(struct inode_security_struct),
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index 91b88f0..a1dbc1c 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -27,14 +27,22 @@
>  #include "flask.h"
>  #include "avc.h"
>  
> +/*
> + * the security parameters associated with the credentials record structure
> + * (struct cred::security)
> + */
> +struct cred_security_struct {
> +	u32	action_sid;	/* perform action as SID */
> +	u32	create_sid;	/* filesystem object creation as SID */
> +	u32	keycreate_sid;	/* key creation as SID */
> +	u32	sockcreate_sid;	/* socket creation as SID */
> +};
> +
>  struct task_security_struct {
>  	struct task_struct *task;      /* back pointer to task object */
>  	u32 osid;            /* SID prior to last execve */
> -	u32 sid;             /* current SID */
> +	u32 victim_sid;	     /* current SID affecting victimisation of this task */
>  	u32 exec_sid;        /* exec SID */
> -	u32 create_sid;      /* fscreate SID */
> -	u32 keycreate_sid;   /* keycreate SID */
> -	u32 sockcreate_sid;  /* fscreate SID */
>  	u32 ptrace_sid;      /* SID of ptrace parent */
>  };
>  
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index c9e92da..9c6737f 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -77,13 +77,13 @@ extern void selnl_notify_setenforce(int val);
>  static int task_has_security(struct task_struct *tsk,
>  			     u32 perms)
>  {
> -	struct task_security_struct *tsec;
> +	struct cred_security_struct *csec;
>  
> -	tsec = tsk->security;
> -	if (!tsec)
> +	csec = tsk->cred->security;
> +	if (!csec)
>  		return -EACCES;
>  
> -	return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
> +	return avc_has_perm(csec->action_sid, SECINITSID_SECURITY,
>  			    SECCLASS_SECURITY, perms, NULL);
>  }
>  
> diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
> index ba715f4..902d302 100644
> --- a/security/selinux/xfrm.c
> +++ b/security/selinux/xfrm.c
> @@ -240,7 +240,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
>  	/*
>  	 * Does the subject have permission to set security context?
>  	 */
> -	rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> +	rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
>  			  SECCLASS_ASSOCIATION,
>  			  ASSOCIATION__SETCONTEXT, NULL);
>  	if (rc)
> @@ -341,7 +341,7 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
>  	int rc = 0;
>  
>  	if (ctx)
> -		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> +		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
>  				  SECCLASS_ASSOCIATION,
>  				  ASSOCIATION__SETCONTEXT, NULL);
>  
> @@ -383,7 +383,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
>  	int rc = 0;
>  
>  	if (ctx)
> -		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> +		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
>  				  SECCLASS_ASSOCIATION,
>  				  ASSOCIATION__SETCONTEXT, NULL);
>  
> 
> -
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-24 14:00   ` Serge E. Hallyn
@ 2007-09-24 14:21     ` Stephen Smalley
  2007-09-24 15:35       ` Serge E. Hallyn
  2007-09-24 15:52       ` David Howells
  2007-09-26 13:30     ` David Howells
  1 sibling, 2 replies; 22+ messages in thread
From: Stephen Smalley @ 2007-09-24 14:21 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: David Howells, viro, hch, Trond.Myklebust, casey, linux-kernel,
	selinux, linux-security-module

On Mon, 2007-09-24 at 09:00 -0500, Serge E. Hallyn wrote:
> Quoting David Howells (dhowells@redhat.com):
> > Move into the cred struct the part of the task security data that defines how a
> > task acts upon an object.  The part that defines how something acts upon a task
> > remains attached to the task.
> > 
> > For SELinux this requires some of task_security_struct to be split off into
> > cred_security_struct which is then attached to struct cred.  Note that the
> > contents of cred_security_struct may not be changed without the generation of a
> > new struct cred.
> > 
> > The split is as follows:
> > 
> >  (*) create_sid, keycreate_sid and sockcreate_sid just move across.
> > 
> >  (*) sid is split into victim_sid - which remains - and action_sid - which
> >      migrates.
> 
> My concern is with this victim_sid.  Whether the concern is valid
> depends on exactly how the other credentials can be used, which isn't
> yet entirely clear to me.
> 
> So my concern is that while a task is acting with alternate creds,
> another task can act upon it based upon victim_sid.  So does this
> open up the possibility for an unprivileged task to ptrace or kill
> a task in the middle of a privileged operation?  Is that somehow
> safe in the way this is used here?

I think that's why they want it - so that things like nfsd and
cachefiles can switch the credentials used for permission checking and
object labeling (actor_sid, fscreate_sid) without exposing them to
access by other tasks via signals, ptrace, etc (victim_sid).  Similar to
fsuid vs. uid.  And then the separate issue of migrating the permission
checking and object labeling state into a separate credential structure
that can have a separate lifecycle from the task.

Precisely when to use one identity vs. the other though isn't always
clear, and the potential for accidental divergence is also a concern.

I think it is a mistake to have selinux_get_task_sid() blindly return
the victim SID btw.  I think you likely need to split that interface and
require the caller to indicate what it wants, so that there is no
accidental misuse of the victim SID where the caller wanted the actor
SID.  Or at least rename that interface to make it clear that it only
returns the victim SID.

> I guess I need to look more at the actual nfs patches etc.
> 
> thanks,
> -serge
> 
> 
> >  (*) osid, exec_sid and ptrace_sid remain.
> > 
> > victim_sid is the SID used to govern actions upon the task.  action_sid is used
> > to govern actions made by the task.
> > 
> > When accessing the cred_security_struct of another process, RCU read procedures
> > must be observed.
> > 
> > Signed-off-by: David Howells <dhowells@redhat.com>
> > ---
> > 
> >  include/linux/cred.h              |    1 
> >  include/linux/security.h          |   34 +++
> >  kernel/cred.c                     |    7 +
> >  security/dummy.c                  |   11 +
> >  security/selinux/exports.c        |    6 
> >  security/selinux/hooks.c          |  497 +++++++++++++++++++++++--------------
> >  security/selinux/include/objsec.h |   16 +
> >  security/selinux/selinuxfs.c      |    8 -
> >  security/selinux/xfrm.c           |    6 
> >  9 files changed, 380 insertions(+), 206 deletions(-)
> > 
> > diff --git a/include/linux/cred.h b/include/linux/cred.h
> > index 22ae610..6c6feec 100644
> > --- a/include/linux/cred.h
> > +++ b/include/linux/cred.h
> > @@ -26,6 +26,7 @@ struct cred {
> >  	gid_t			gid;		/* fsgid as was */
> >  	struct rcu_head		exterminate;	/* cred destroyer */
> >  	struct group_info	*group_info;
> > +	void			*security;
> >  
> >  	/* caches for references to the three task keyrings
> >  	 * - note that key_ref_t isn't typedef'd at this point, hence the odd
> > diff --git a/include/linux/security.h b/include/linux/security.h
> > index 1a15526..e5ed2ea 100644
> > --- a/include/linux/security.h
> > +++ b/include/linux/security.h
> > @@ -504,6 +504,18 @@ struct request_sock;
> >   *	@file contains the file structure being received.
> >   *	Return 0 if permission is granted.
> >   *
> > + * Security hooks for credential structure operations.
> > + *
> > + * @cred_dup:
> > + *	Duplicate the credentials onto a duplicated cred structure.
> > + *	@cred points to the credentials structure.  cred->security points to the
> > + *	security struct that was attached to the original cred struct, but it
> > + *	lacks a reference for the duplication if reference counting is needed.
> > + *
> > + * @cred_destroy:
> > + *	Destroy the credentials attached to a cred structure.
> > + *	@cred points to the credentials structure that is to be destroyed.
> > + *
> >   * Security hooks for task operations.
> >   *
> >   * @task_create:
> > @@ -1257,6 +1269,9 @@ struct security_operations {
> >  				    struct fown_struct * fown, int sig);
> >  	int (*file_receive) (struct file * file);
> >  
> > +	int (*cred_dup)(struct cred *cred);
> > +	void (*cred_destroy)(struct cred *cred);
> > +
> >  	int (*task_create) (unsigned long clone_flags);
> >  	int (*task_alloc_security) (struct task_struct * p);
> >  	void (*task_free_security) (struct task_struct * p);
> > @@ -1864,6 +1879,16 @@ static inline int security_file_receive (struct file *file)
> >  	return security_ops->file_receive (file);
> >  }
> >  
> > +static inline int security_cred_dup(struct cred *cred)
> > +{
> > +	return security_ops->cred_dup(cred);
> > +}
> > +
> > +static inline void security_cred_destroy(struct cred *cred)
> > +{
> > +	return security_ops->cred_destroy(cred);
> > +}
> > +
> >  static inline int security_task_create (unsigned long clone_flags)
> >  {
> >  	return security_ops->task_create (clone_flags);
> > @@ -2546,6 +2571,15 @@ static inline int security_file_receive (struct file *file)
> >  	return 0;
> >  }
> >  
> > +static inline int security_cred_dup(struct cred *cred)
> > +{
> > +	return 0;
> > +}
> > +
> > +static inline void security_cred_destroy(struct cred *cred)
> > +{
> > +}
> > +
> >  static inline int security_task_create (unsigned long clone_flags)
> >  {
> >  	return 0;
> > diff --git a/kernel/cred.c b/kernel/cred.c
> > index e96dafe..6a9dda2 100644
> > --- a/kernel/cred.c
> > +++ b/kernel/cred.c
> > @@ -92,6 +92,12 @@ struct cred *dup_cred(const struct cred *pcred)
> >  	if (likely(cred)) {
> >  		*cred = *pcred;
> >  		atomic_set(&cred->usage, 1);
> > +
> > +		if (security_cred_dup(cred) < 0) {
> > +			kfree(cred);
> > +			return NULL;
> > +		}
> > +
> >  		get_group_info(cred->group_info);
> >  		key_get(key_ref_to_ptr(cred->session_keyring));
> >  		key_get(key_ref_to_ptr(cred->process_keyring));
> > @@ -109,6 +115,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
> >  {
> >  	struct cred *cred = container_of(rcu, struct cred, exterminate);
> >  
> > +	security_cred_destroy(cred);
> >  	put_group_info(cred->group_info);
> >  	key_ref_put(cred->session_keyring);
> >  	key_ref_put(cred->process_keyring);
> > diff --git a/security/dummy.c b/security/dummy.c
> > index 62de89c..f535cc6 100644
> > --- a/security/dummy.c
> > +++ b/security/dummy.c
> > @@ -468,6 +468,15 @@ static int dummy_file_receive (struct file *file)
> >  	return 0;
> >  }
> >  
> > +static int dummy_cred_dup(struct cred *cred)
> > +{
> > +	return 0;
> > +}
> > +
> > +static void dummy_cred_destroy(struct cred *cred)
> > +{
> > +}
> > +
> >  static int dummy_task_create (unsigned long clone_flags)
> >  {
> >  	return 0;
> > @@ -1038,6 +1047,8 @@ void security_fixup_ops (struct security_operations *ops)
> >  	set_to_dummy_if_null(ops, file_set_fowner);
> >  	set_to_dummy_if_null(ops, file_send_sigiotask);
> >  	set_to_dummy_if_null(ops, file_receive);
> > +	set_to_dummy_if_null(ops, cred_dup);
> > +	set_to_dummy_if_null(ops, cred_destroy);
> >  	set_to_dummy_if_null(ops, task_create);
> >  	set_to_dummy_if_null(ops, task_alloc_security);
> >  	set_to_dummy_if_null(ops, task_free_security);
> > diff --git a/security/selinux/exports.c b/security/selinux/exports.c
> > index b6f9694..29cb87a 100644
> > --- a/security/selinux/exports.c
> > +++ b/security/selinux/exports.c
> > @@ -57,7 +57,7 @@ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
> >  {
> >  	if (selinux_enabled) {
> >  		struct task_security_struct *tsec = tsk->security;
> > -		*sid = tsec->sid;
> > +		*sid = tsec->victim_sid;
> >  		return;
> >  	}
> >  	*sid = 0;
> > @@ -77,9 +77,9 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
> >  int selinux_relabel_packet_permission(u32 sid)
> >  {
> >  	if (selinux_enabled) {
> > -		struct task_security_struct *tsec = current->security;
> > +		struct cred_security_struct *csec = current->cred->security;
> >  
> > -		return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
> > +		return avc_has_perm(csec->action_sid, sid, SECCLASS_PACKET,
> >  				    PACKET__RELABELTO, NULL);
> >  	}
> >  	return 0;
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index 3694662..6fc41da 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -162,7 +162,8 @@ static int task_alloc_security(struct task_struct *task)
> >  		return -ENOMEM;
> >  
> >  	tsec->task = task;
> > -	tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
> > +	tsec->osid = tsec->victim_sid = tsec->ptrace_sid =
> > +		SECINITSID_UNLABELED;
> >  	task->security = tsec;
> >  
> >  	return 0;
> > @@ -177,7 +178,7 @@ static void task_free_security(struct task_struct *task)
> >  
> >  static int inode_alloc_security(struct inode *inode)
> >  {
> > -	struct task_security_struct *tsec = current->security;
> > +	struct cred_security_struct *csec = current->cred->security;
> >  	struct inode_security_struct *isec;
> >  
> >  	isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL);
> > @@ -189,7 +190,7 @@ static int inode_alloc_security(struct inode *inode)
> >  	isec->inode = inode;
> >  	isec->sid = SECINITSID_UNLABELED;
> >  	isec->sclass = SECCLASS_FILE;
> > -	isec->task_sid = tsec->sid;
> > +	isec->task_sid = csec->action_sid;
> >  	inode->i_security = isec;
> >  
> >  	return 0;
> > @@ -211,7 +212,7 @@ static void inode_free_security(struct inode *inode)
> >  
> >  static int file_alloc_security(struct file *file)
> >  {
> > -	struct task_security_struct *tsec = current->security;
> > +	struct cred_security_struct *csec = current->cred->security;
> >  	struct file_security_struct *fsec;
> >  
> >  	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
> > @@ -219,8 +220,8 @@ static int file_alloc_security(struct file *file)
> >  		return -ENOMEM;
> >  
> >  	fsec->file = file;
> > -	fsec->sid = tsec->sid;
> > -	fsec->fown_sid = tsec->sid;
> > +	fsec->sid = csec->action_sid;
> > +	fsec->fown_sid = csec->action_sid;
> >  	file->f_security = fsec;
> >  
> >  	return 0;
> > @@ -333,26 +334,26 @@ static match_table_t tokens = {
> >  
> >  static int may_context_mount_sb_relabel(u32 sid,
> >  			struct superblock_security_struct *sbsec,
> > -			struct task_security_struct *tsec)
> > +			struct cred_security_struct *csec)
> >  {
> >  	int rc;
> >  
> > -	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > +	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
> >  			  FILESYSTEM__RELABELFROM, NULL);
> >  	if (rc)
> >  		return rc;
> >  
> > -	rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
> > +	rc = avc_has_perm(csec->action_sid, sid, SECCLASS_FILESYSTEM,
> >  			  FILESYSTEM__RELABELTO, NULL);
> >  	return rc;
> >  }
> >  
> >  static int may_context_mount_inode_relabel(u32 sid,
> >  			struct superblock_security_struct *sbsec,
> > -			struct task_security_struct *tsec)
> > +			struct cred_security_struct *csec)
> >  {
> >  	int rc;
> > -	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > +	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
> >  			  FILESYSTEM__RELABELFROM, NULL);
> >  	if (rc)
> >  		return rc;
> > @@ -369,7 +370,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> >  	const char *name;
> >  	u32 sid;
> >  	int alloc = 0, rc = 0, seen = 0;
> > -	struct task_security_struct *tsec = current->security;
> > +	struct cred_security_struct *csec = current->cred->security;
> >  	struct superblock_security_struct *sbsec = sb->s_security;
> >  
> >  	if (!data)
> > @@ -501,7 +502,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> >  			goto out_free;
> >  		}
> >  
> > -		rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> > +		rc = may_context_mount_sb_relabel(sid, sbsec, csec);
> >  		if (rc)
> >  			goto out_free;
> >  
> > @@ -523,12 +524,12 @@ static int try_context_mount(struct super_block *sb, void *data)
> >  		}
> >  
> >  		if (!fscontext) {
> > -			rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> > +			rc = may_context_mount_sb_relabel(sid, sbsec, csec);
> >  			if (rc)
> >  				goto out_free;
> >  			sbsec->sid = sid;
> >  		} else {
> > -			rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> > +			rc = may_context_mount_inode_relabel(sid, sbsec, csec);
> >  			if (rc)
> >  				goto out_free;
> >  		}
> > @@ -548,7 +549,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> >  			goto out_free;
> >  		}
> >  
> > -		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> > +		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
> >  		if (rc)
> >  			goto out_free;
> >  
> > @@ -568,7 +569,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> >  		if (sid == sbsec->def_sid)
> >  			goto out_free;
> >  
> > -		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> > +		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
> >  		if (rc)
> >  			goto out_free;
> >  
> > @@ -1023,15 +1024,22 @@ static inline u32 signal_to_av(int sig)
> >  
> >  /* Check permission betweeen a pair of tasks, e.g. signal checks,
> >     fork check, ptrace check, etc. */
> > -static int task_has_perm(struct task_struct *tsk1,
> > -			 struct task_struct *tsk2,
> > +static int task_has_perm(struct task_struct *actor,
> > +			 struct task_struct *victim,
> >  			 u32 perms)
> >  {
> > -	struct task_security_struct *tsec1, *tsec2;
> > +	struct cred_security_struct *csec;
> > +	struct task_security_struct *tsec;
> > +	u32 action_sid;
> > +
> > +	/* the actor may not be the current task */
> > +	rcu_read_lock();
> > +	csec = task_cred(actor)->security;
> > +	action_sid = csec->action_sid;
> > +	rcu_read_unlock();
> >  
> > -	tsec1 = tsk1->security;
> > -	tsec2 = tsk2->security;
> > -	return avc_has_perm(tsec1->sid, tsec2->sid,
> > +	tsec = victim->security;
> > +	return avc_has_perm(action_sid, tsec->victim_sid,
> >  			    SECCLASS_PROCESS, perms, NULL);
> >  }
> >  
> > @@ -1039,16 +1047,16 @@ static int task_has_perm(struct task_struct *tsk1,
> >  static int task_has_capability(struct task_struct *tsk,
> >  			       int cap)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct avc_audit_data ad;
> >  
> > -	tsec = tsk->security;
> > +	csec = tsk->cred->security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad,CAP);
> >  	ad.tsk = tsk;
> >  	ad.u.cap = cap;
> >  
> > -	return avc_has_perm(tsec->sid, tsec->sid,
> > +	return avc_has_perm(csec->action_sid, csec->action_sid,
> >  			    SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
> >  }
> >  
> > @@ -1056,11 +1064,11 @@ static int task_has_capability(struct task_struct *tsk,
> >  static int task_has_system(struct task_struct *tsk,
> >  			   u32 perms)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  
> > -	tsec = tsk->security;
> > +	csec = tsk->cred->security;
> >  
> > -	return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
> > +	return avc_has_perm(csec->action_sid, SECINITSID_KERNEL,
> >  			    SECCLASS_SYSTEM, perms, NULL);
> >  }
> >  
> > @@ -1072,14 +1080,14 @@ static int inode_has_perm(struct task_struct *tsk,
> >  			  u32 perms,
> >  			  struct avc_audit_data *adp)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct inode_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  
> >  	if (unlikely (IS_PRIVATE (inode)))
> >  		return 0;
> >  
> > -	tsec = tsk->security;
> > +	csec = tsk->cred->security;
> >  	isec = inode->i_security;
> >  
> >  	if (!adp) {
> > @@ -1088,7 +1096,8 @@ static int inode_has_perm(struct task_struct *tsk,
> >  		ad.u.fs.inode = inode;
> >  	}
> >  
> > -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
> > +	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> > +			    adp);
> >  }
> >  
> >  /* Same as inode_has_perm, but pass explicit audit data containing
> > @@ -1119,7 +1128,7 @@ static int file_has_perm(struct task_struct *tsk,
> >  				struct file *file,
> >  				u32 av)
> >  {
> > -	struct task_security_struct *tsec = tsk->security;
> > +	struct cred_security_struct *csec = tsk->cred->security;
> >  	struct file_security_struct *fsec = file->f_security;
> >  	struct vfsmount *mnt = file->f_path.mnt;
> >  	struct dentry *dentry = file->f_path.dentry;
> > @@ -1131,8 +1140,8 @@ static int file_has_perm(struct task_struct *tsk,
> >  	ad.u.fs.mnt = mnt;
> >  	ad.u.fs.dentry = dentry;
> >  
> > -	if (tsec->sid != fsec->sid) {
> > -		rc = avc_has_perm(tsec->sid, fsec->sid,
> > +	if (csec->action_sid != fsec->sid) {
> > +		rc = avc_has_perm(csec->action_sid, fsec->sid,
> >  				  SECCLASS_FD,
> >  				  FD__USE,
> >  				  &ad);
> > @@ -1152,36 +1161,36 @@ static int may_create(struct inode *dir,
> >  		      struct dentry *dentry,
> >  		      u16 tclass)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct inode_security_struct *dsec;
> >  	struct superblock_security_struct *sbsec;
> >  	u32 newsid;
> >  	struct avc_audit_data ad;
> >  	int rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	dsec = dir->i_security;
> >  	sbsec = dir->i_sb->s_security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, FS);
> >  	ad.u.fs.dentry = dentry;
> >  
> > -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
> > +	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR,
> >  			  DIR__ADD_NAME | DIR__SEARCH,
> >  			  &ad);
> >  	if (rc)
> >  		return rc;
> >  
> > -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > -		newsid = tsec->create_sid;
> > +	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > +		newsid = csec->create_sid;
> >  	} else {
> > -		rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
> > -					     &newsid);
> > +		rc = security_transition_sid(csec->action_sid, dsec->sid,
> > +					     tclass, &newsid);
> >  		if (rc)
> >  			return rc;
> >  	}
> >  
> > -	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
> > +	rc = avc_has_perm(csec->action_sid, newsid, tclass, FILE__CREATE, &ad);
> >  	if (rc)
> >  		return rc;
> >  
> > @@ -1194,11 +1203,12 @@ static int may_create(struct inode *dir,
> >  static int may_create_key(u32 ksid,
> >  			  struct task_struct *ctx)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  
> > -	tsec = ctx->security;
> > +	csec = ctx->cred->security;
> >  
> > -	return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
> > +	return avc_has_perm(csec->action_sid, ksid, SECCLASS_KEY, KEY__CREATE,
> > +			    NULL);
> >  }
> >  
> >  #define MAY_LINK   0
> > @@ -1211,13 +1221,13 @@ static int may_link(struct inode *dir,
> >  		    int kind)
> >  
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct inode_security_struct *dsec, *isec;
> >  	struct avc_audit_data ad;
> >  	u32 av;
> >  	int rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	dsec = dir->i_security;
> >  	isec = dentry->d_inode->i_security;
> >  
> > @@ -1226,7 +1236,7 @@ static int may_link(struct inode *dir,
> >  
> >  	av = DIR__SEARCH;
> >  	av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
> > -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
> > +	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR, av, &ad);
> >  	if (rc)
> >  		return rc;
> >  
> > @@ -1245,7 +1255,7 @@ static int may_link(struct inode *dir,
> >  		return 0;
> >  	}
> >  
> > -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
> > +	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, av, &ad);
> >  	return rc;
> >  }
> >  
> > @@ -1254,14 +1264,14 @@ static inline int may_rename(struct inode *old_dir,
> >  			     struct inode *new_dir,
> >  			     struct dentry *new_dentry)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
> >  	struct avc_audit_data ad;
> >  	u32 av;
> >  	int old_is_dir, new_is_dir;
> >  	int rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	old_dsec = old_dir->i_security;
> >  	old_isec = old_dentry->d_inode->i_security;
> >  	old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
> > @@ -1270,16 +1280,16 @@ static inline int may_rename(struct inode *old_dir,
> >  	AVC_AUDIT_DATA_INIT(&ad, FS);
> >  
> >  	ad.u.fs.dentry = old_dentry;
> > -	rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
> > +	rc = avc_has_perm(csec->action_sid, old_dsec->sid, SECCLASS_DIR,
> >  			  DIR__REMOVE_NAME | DIR__SEARCH, &ad);
> >  	if (rc)
> >  		return rc;
> > -	rc = avc_has_perm(tsec->sid, old_isec->sid,
> > +	rc = avc_has_perm(csec->action_sid, old_isec->sid,
> >  			  old_isec->sclass, FILE__RENAME, &ad);
> >  	if (rc)
> >  		return rc;
> >  	if (old_is_dir && new_dir != old_dir) {
> > -		rc = avc_has_perm(tsec->sid, old_isec->sid,
> > +		rc = avc_has_perm(csec->action_sid, old_isec->sid,
> >  				  old_isec->sclass, DIR__REPARENT, &ad);
> >  		if (rc)
> >  			return rc;
> > @@ -1289,15 +1299,17 @@ static inline int may_rename(struct inode *old_dir,
> >  	av = DIR__ADD_NAME | DIR__SEARCH;
> >  	if (new_dentry->d_inode)
> >  		av |= DIR__REMOVE_NAME;
> > -	rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
> > +	rc = avc_has_perm(csec->action_sid, new_dsec->sid, SECCLASS_DIR, av,
> > +			  &ad);
> >  	if (rc)
> >  		return rc;
> >  	if (new_dentry->d_inode) {
> >  		new_isec = new_dentry->d_inode->i_security;
> >  		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
> > -		rc = avc_has_perm(tsec->sid, new_isec->sid,
> > +		rc = avc_has_perm(csec->action_sid, new_isec->sid,
> >  				  new_isec->sclass,
> > -				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
> > +				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
> > +				  &ad);
> >  		if (rc)
> >  			return rc;
> >  	}
> > @@ -1311,12 +1323,12 @@ static int superblock_has_perm(struct task_struct *tsk,
> >  			       u32 perms,
> >  			       struct avc_audit_data *ad)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct superblock_security_struct *sbsec;
> >  
> > -	tsec = tsk->security;
> > +	csec = tsk->cred->security;
> >  	sbsec = sb->s_security;
> > -	return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > +	return avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
> >  			    perms, ad);
> >  }
> >  
> > @@ -1369,7 +1381,7 @@ static inline u32 file_to_av(struct file *file)
> >  
> >  static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
> >  {
> > -	struct task_security_struct *psec = parent->security;
> > +	struct cred_security_struct *psec;
> >  	struct task_security_struct *csec = child->security;
> >  	int rc;
> >  
> > @@ -1379,8 +1391,12 @@ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
> >  
> >  	rc = task_has_perm(parent, child, PROCESS__PTRACE);
> >  	/* Save the SID of the tracing process for later use in apply_creds. */
> > -	if (!(child->ptrace & PT_PTRACED) && !rc)
> > -		csec->ptrace_sid = psec->sid;
> > +	if (!(child->ptrace & PT_PTRACED) && !rc) {
> > +		rcu_read_lock();
> > +		psec = task_cred(parent)->security;
> > +		csec->ptrace_sid = psec->action_sid;
> > +		rcu_read_unlock();
> > +	}
> >  	return rc;
> >  }
> >  
> > @@ -1470,7 +1486,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> >  {
> >  	int error = 0;
> >  	u32 av;
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	u32 tsid;
> >  	int rc;
> >  
> > @@ -1478,7 +1494,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> >  	if (rc)
> >  		return rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  
> >  	rc = selinux_sysctl_get_sid(table, (op == 0001) ?
> >  				    SECCLASS_DIR : SECCLASS_FILE, &tsid);
> > @@ -1490,7 +1506,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> >  	/* The op values are "defined" in sysctl.c, thereby creating
> >  	 * a bad coupling between this module and sysctl.c */
> >  	if(op == 001) {
> > -		error = avc_has_perm(tsec->sid, tsid,
> > +		error = avc_has_perm(csec->action_sid, tsid,
> >  				     SECCLASS_DIR, DIR__SEARCH, NULL);
> >  	} else {
> >  		av = 0;
> > @@ -1499,7 +1515,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> >  		if (op & 002)
> >  			av |= FILE__WRITE;
> >  		if (av)
> > -			error = avc_has_perm(tsec->sid, tsid,
> > +			error = avc_has_perm(csec->action_sid, tsid,
> >  					     SECCLASS_FILE, av, NULL);
> >          }
> >  
> > @@ -1587,11 +1603,11 @@ static int selinux_syslog(int type)
> >  static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
> >  {
> >  	int rc, cap_sys_admin = 0;
> > -	struct task_security_struct *tsec = current->security;
> > +	struct cred_security_struct *csec = current->cred->security;
> >  
> >  	rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
> >  	if (rc == 0)
> > -		rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
> > +		rc = avc_has_perm_noaudit(csec->action_sid, csec->action_sid,
> >  					  SECCLASS_CAPABILITY,
> >  					  CAP_TO_MASK(CAP_SYS_ADMIN),
> >  					  0,
> > @@ -1624,6 +1640,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
> >  static int selinux_bprm_set_security(struct linux_binprm *bprm)
> >  {
> >  	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct inode *inode = bprm->file->f_path.dentry->d_inode;
> >  	struct inode_security_struct *isec;
> >  	struct bprm_security_struct *bsec;
> > @@ -1641,15 +1658,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
> >  		return 0;
> >  
> >  	tsec = current->security;
> > +	csec = bprm->cred->security;
> >  	isec = inode->i_security;
> >  
> >  	/* Default to the current task SID. */
> > -	bsec->sid = tsec->sid;
> > +	bsec->sid = csec->action_sid;
> >  
> >  	/* Reset fs, key, and sock SIDs on execve. */
> > -	tsec->create_sid = 0;
> > -	tsec->keycreate_sid = 0;
> > -	tsec->sockcreate_sid = 0;
> > +	csec->create_sid = 0;
> > +	csec->keycreate_sid = 0;
> > +	csec->sockcreate_sid = 0;
> >  
> >  	if (tsec->exec_sid) {
> >  		newsid = tsec->exec_sid;
> > @@ -1657,7 +1675,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
> >  		tsec->exec_sid = 0;
> >  	} else {
> >  		/* Check for a default transition on this program. */
> > -		rc = security_transition_sid(tsec->sid, isec->sid,
> > +		rc = security_transition_sid(csec->action_sid, isec->sid,
> >  		                             SECCLASS_PROCESS, &newsid);
> >  		if (rc)
> >  			return rc;
> > @@ -1668,16 +1686,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
> >  	ad.u.fs.dentry = bprm->file->f_path.dentry;
> >  
> >  	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
> > -		newsid = tsec->sid;
> > +		newsid = csec->action_sid;
> >  
> > -        if (tsec->sid == newsid) {
> > -		rc = avc_has_perm(tsec->sid, isec->sid,
> > +        if (csec->action_sid == newsid) {
> > +		rc = avc_has_perm(csec->action_sid, isec->sid,
> >  				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
> >  		if (rc)
> >  			return rc;
> >  	} else {
> >  		/* Check permissions for the transition. */
> > -		rc = avc_has_perm(tsec->sid, newsid,
> > +		rc = avc_has_perm(csec->action_sid, newsid,
> >  				  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
> >  		if (rc)
> >  			return rc;
> > @@ -1709,11 +1727,11 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm)
> >  	struct task_security_struct *tsec = current->security;
> >  	int atsecure = 0;
> >  
> > -	if (tsec->osid != tsec->sid) {
> > +	if (tsec->osid != tsec->victim_sid) {
> >  		/* Enable secure mode for SIDs transitions unless
> >  		   the noatsecure permission is granted between
> >  		   the two SIDs, i.e. ahp returns 0. */
> > -		atsecure = avc_has_perm(tsec->osid, tsec->sid,
> > +		atsecure = avc_has_perm(tsec->osid, tsec->victim_sid,
> >  					 SECCLASS_PROCESS,
> >  					 PROCESS__NOATSECURE, NULL);
> >  	}
> > @@ -1823,6 +1841,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
> >  static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
> >  {
> >  	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct bprm_security_struct *bsec;
> >  	u32 sid;
> >  	int rc;
> > @@ -1830,17 +1849,17 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
> >  	secondary_ops->bprm_apply_creds(bprm, unsafe);
> >  
> >  	tsec = current->security;
> > -
> > +	csec = bprm->cred->security;
> >  	bsec = bprm->security;
> >  	sid = bsec->sid;
> >  
> > -	tsec->osid = tsec->sid;
> > +	tsec->osid = tsec->victim_sid;
> >  	bsec->unsafe = 0;
> > -	if (tsec->sid != sid) {
> > +	if (tsec->victim_sid != sid) {
> >  		/* Check for shared state.  If not ok, leave SID
> >  		   unchanged and kill. */
> >  		if (unsafe & LSM_UNSAFE_SHARE) {
> > -			rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
> > +			rc = avc_has_perm(tsec->victim_sid, sid, SECCLASS_PROCESS,
> >  					PROCESS__SHARE, NULL);
> >  			if (rc) {
> >  				bsec->unsafe = 1;
> > @@ -1859,7 +1878,9 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
> >  				return;
> >  			}
> >  		}
> > -		tsec->sid = sid;
> > +		if (csec->action_sid == tsec->victim_sid)
> > +			csec->action_sid = sid;
> > +		tsec->victim_sid = sid;
> >  	}
> >  }
> >  
> > @@ -1881,7 +1902,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
> >  		force_sig_specific(SIGKILL, current);
> >  		return;
> >  	}
> > -	if (tsec->osid == tsec->sid)
> > +	if (tsec->osid == tsec->victim_sid)
> >  		return;
> >  
> >  	/* Close files for which the new task SID is not authorized. */
> > @@ -1893,7 +1914,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
> >  	   signals. This must occur _after_ the task SID has
> >  	  been updated so that any kill done after the flush
> >  	  will be checked against the new SID. */
> > -	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
> > +	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
> >  			  PROCESS__SIGINH, NULL);
> >  	if (rc) {
> >  		memset(&itimer, 0, sizeof itimer);
> > @@ -1920,7 +1941,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
> >  	   than the default soft limit for cases where the default
> >  	   is lower than the hard limit, e.g. RLIMIT_CORE or
> >  	   RLIMIT_STACK.*/
> > -	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
> > +	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
> >  			  PROCESS__RLIMITINH, NULL);
> >  	if (rc) {
> >  		for (i = 0; i < RLIM_NLIMITS; i++) {
> > @@ -2122,21 +2143,21 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
> >  				       char **name, void **value,
> >  				       size_t *len)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct inode_security_struct *dsec;
> >  	struct superblock_security_struct *sbsec;
> >  	u32 newsid, clen;
> >  	int rc;
> >  	char *namep = NULL, *context;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	dsec = dir->i_security;
> >  	sbsec = dir->i_sb->s_security;
> >  
> > -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > -		newsid = tsec->create_sid;
> > +	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > +		newsid = csec->create_sid;
> >  	} else {
> > -		rc = security_transition_sid(tsec->sid, dsec->sid,
> > +		rc = security_transition_sid(csec->action_sid, dsec->sid,
> >  					     inode_mode_to_security_class(inode->i_mode),
> >  					     &newsid);
> >  		if (rc) {
> > @@ -2295,7 +2316,7 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
> >  
> >  static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
> >  {
> > -	struct task_security_struct *tsec = current->security;
> > +	struct cred_security_struct *csec = current->cred->security;
> >  	struct inode *inode = dentry->d_inode;
> >  	struct inode_security_struct *isec = inode->i_security;
> >  	struct superblock_security_struct *sbsec;
> > @@ -2327,7 +2348,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
> >  	AVC_AUDIT_DATA_INIT(&ad,FS);
> >  	ad.u.fs.dentry = dentry;
> >  
> > -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
> > +	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass,
> >  			  FILE__RELABELFROM, &ad);
> >  	if (rc)
> >  		return rc;
> > @@ -2336,12 +2357,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
> >  	if (rc)
> >  		return rc;
> >  
> > -	rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
> > +	rc = avc_has_perm(csec->action_sid, newsid, isec->sclass,
> >  			  FILE__RELABELTO, &ad);
> >  	if (rc)
> >  		return rc;
> >  
> > -	rc = security_validate_transition(isec->sid, newsid, tsec->sid,
> > +	rc = security_validate_transition(isec->sid, newsid, csec->action_sid,
> >  	                                  isec->sclass);
> >  	if (rc)
> >  		return rc;
> > @@ -2575,8 +2596,9 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
> >  			     unsigned long prot, unsigned long flags,
> >  			     unsigned long addr, unsigned long addr_only)
> >  {
> > +	struct cred_security_struct *csec = current->cred->security;
> >  	int rc = 0;
> > -	u32 sid = ((struct task_security_struct*)(current->security))->sid;
> > +	u32 sid = csec->action_sid;
> >  
> >  	if (addr < mmap_min_addr)
> >  		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
> > @@ -2690,7 +2712,7 @@ static int selinux_file_set_fowner(struct file *file)
> >  
> >  	tsec = current->security;
> >  	fsec = file->f_security;
> > -	fsec->fown_sid = tsec->sid;
> > +	fsec->fown_sid = tsec->victim_sid;
> >  
> >  	return 0;
> >  }
> > @@ -2714,7 +2736,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
> >  	else
> >  		perm = signal_to_av(signum);
> >  
> > -	return avc_has_perm(fsec->fown_sid, tsec->sid,
> > +	return avc_has_perm(fsec->fown_sid, tsec->victim_sid,
> >  			    SECCLASS_PROCESS, perm, NULL);
> >  }
> >  
> > @@ -2723,6 +2745,31 @@ static int selinux_file_receive(struct file *file)
> >  	return file_has_perm(current, file, file_to_av(file));
> >  }
> >  
> > +/* credential security operations */
> > +
> > +/*
> > + * duplicate the security information attached to a credentials record that is
> > + * itself undergoing duplication
> > + */
> > +static int selinux_cred_dup(struct cred *cred)
> > +{
> > +	cred->security = kmemdup(cred->security,
> > +				 sizeof(struct cred_security_struct),
> > +				 GFP_KERNEL);
> > +	return cred->security ? 0 : -ENOMEM;
> > +}
> > +
> > +/*
> > + * destroy the security information attached to a credentials record
> > + * - this is done under RCU, and may not be associated with the task that set it
> > + *   up
> > + */
> > +static void selinux_cred_destroy(struct cred *cred)
> > +{
> > +	kfree(cred->security);
> > +}
> > +
> > +
> >  /* task security operations */
> >  
> >  static int selinux_task_create(unsigned long clone_flags)
> > @@ -2749,13 +2796,10 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
> >  	tsec2 = tsk->security;
> >  
> >  	tsec2->osid = tsec1->osid;
> > -	tsec2->sid = tsec1->sid;
> > +	tsec2->victim_sid = tsec1->victim_sid;
> >  
> > -	/* Retain the exec, fs, key, and sock SIDs across fork */
> > +	/* Retain the exec SID across fork */
> >  	tsec2->exec_sid = tsec1->exec_sid;
> > -	tsec2->create_sid = tsec1->create_sid;
> > -	tsec2->keycreate_sid = tsec1->keycreate_sid;
> > -	tsec2->sockcreate_sid = tsec1->sockcreate_sid;
> >  
> >  	/* Retain ptracer SID across fork, if any.
> >  	   This will be reset by the ptrace hook upon any
> > @@ -2893,7 +2937,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
> >  		perm = signal_to_av(sig);
> >  	tsec = p->security;
> >  	if (secid)
> > -		rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
> > +		rc = avc_has_perm(secid, tsec->victim_sid,
> > +				  SECCLASS_PROCESS, perm, NULL);
> >  	else
> >  		rc = task_has_perm(current, p, perm);
> >  	return rc;
> > @@ -2927,8 +2972,8 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
> >  	secondary_ops->task_reparent_to_init(p);
> >  
> >  	tsec = p->security;
> > -	tsec->osid = tsec->sid;
> > -	tsec->sid = SECINITSID_KERNEL;
> > +	tsec->osid = tsec->victim_sid;
> > +	tsec->victim_sid = SECINITSID_KERNEL;
> >  	return;
> >  }
> >  
> > @@ -2938,7 +2983,7 @@ static void selinux_task_to_inode(struct task_struct *p,
> >  	struct task_security_struct *tsec = p->security;
> >  	struct inode_security_struct *isec = inode->i_security;
> >  
> > -	isec->sid = tsec->sid;
> > +	isec->sid = tsec->victim_sid;
> >  	isec->initialized = 1;
> >  	return;
> >  }
> > @@ -3163,11 +3208,11 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
> >  			   u32 perms)
> >  {
> >  	struct inode_security_struct *isec;
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct avc_audit_data ad;
> >  	int err = 0;
> >  
> > -	tsec = task->security;
> > +	csec = task->cred->security;
> >  	isec = SOCK_INODE(sock)->i_security;
> >  
> >  	if (isec->sid == SECINITSID_KERNEL)
> > @@ -3175,7 +3220,8 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad,NET);
> >  	ad.u.net.sk = sock->sk;
> > -	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
> > +	err = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> > +			   &ad);
> >  
> >  out:
> >  	return err;
> > @@ -3185,15 +3231,15 @@ static int selinux_socket_create(int family, int type,
> >  				 int protocol, int kern)
> >  {
> >  	int err = 0;
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	u32 newsid;
> >  
> >  	if (kern)
> >  		goto out;
> >  
> > -	tsec = current->security;
> > -	newsid = tsec->sockcreate_sid ? : tsec->sid;
> > -	err = avc_has_perm(tsec->sid, newsid,
> > +	csec = current->cred->security;
> > +	newsid = csec->sockcreate_sid ? : csec->action_sid;
> > +	err = avc_has_perm(csec->action_sid, newsid,
> >  			   socket_type_to_security_class(family, type,
> >  			   protocol), SOCKET__CREATE, NULL);
> >  
> > @@ -3206,14 +3252,14 @@ static int selinux_socket_post_create(struct socket *sock, int family,
> >  {
> >  	int err = 0;
> >  	struct inode_security_struct *isec;
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct sk_security_struct *sksec;
> >  	u32 newsid;
> >  
> >  	isec = SOCK_INODE(sock)->i_security;
> >  
> > -	tsec = current->security;
> > -	newsid = tsec->sockcreate_sid ? : tsec->sid;
> > +	csec = current->cred->security;
> > +	newsid = csec->sockcreate_sid ? : csec->action_sid;
> >  	isec->sclass = socket_type_to_security_class(family, type, protocol);
> >  	isec->sid = kern ? SECINITSID_KERNEL : newsid;
> >  	isec->initialized = 1;
> > @@ -4027,7 +4073,7 @@ static int ipc_alloc_security(struct task_struct *task,
> >  			      struct kern_ipc_perm *perm,
> >  			      u16 sclass)
> >  {
> > -	struct task_security_struct *tsec = task->security;
> > +	struct cred_security_struct *csec = task->cred->security;
> >  	struct ipc_security_struct *isec;
> >  
> >  	isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
> > @@ -4036,7 +4082,7 @@ static int ipc_alloc_security(struct task_struct *task,
> >  
> >  	isec->sclass = sclass;
> >  	isec->ipc_perm = perm;
> > -	isec->sid = tsec->sid;
> > +	isec->sid = csec->action_sid;
> >  	perm->security = isec;
> >  
> >  	return 0;
> > @@ -4075,17 +4121,18 @@ static void msg_msg_free_security(struct msg_msg *msg)
> >  static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
> >  			u32 perms)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = ipc_perms->security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >  	ad.u.ipc_id = ipc_perms->key;
> >  
> > -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
> > +	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> > +			    &ad);
> >  }
> >  
> >  static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
> > @@ -4101,7 +4148,7 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
> >  /* message queue security operations */
> >  static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  	int rc;
> > @@ -4110,13 +4157,13 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
> >  	if (rc)
> >  		return rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = msq->q_perm.security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >   	ad.u.ipc_id = msq->q_perm.key;
> >  
> > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
> >  			  MSGQ__CREATE, &ad);
> >  	if (rc) {
> >  		ipc_free_security(&msq->q_perm);
> > @@ -4132,17 +4179,17 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
> >  
> >  static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = msq->q_perm.security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >  	ad.u.ipc_id = msq->q_perm.key;
> >  
> > -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> > +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
> >  			    MSGQ__ASSOCIATE, &ad);
> >  }
> >  
> > @@ -4176,13 +4223,13 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
> >  
> >  static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct msg_security_struct *msec;
> >  	struct avc_audit_data ad;
> >  	int rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = msq->q_perm.security;
> >  	msec = msg->security;
> >  
> > @@ -4194,7 +4241,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
> >  		 * Compute new sid based on current process and
> >  		 * message queue this message will be stored in
> >  		 */
> > -		rc = security_transition_sid(tsec->sid,
> > +		rc = security_transition_sid(csec->action_sid,
> >  					     isec->sid,
> >  					     SECCLASS_MSG,
> >  					     &msec->sid);
> > @@ -4206,11 +4253,11 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
> >  	ad.u.ipc_id = msq->q_perm.key;
> >  
> >  	/* Can this process write to the queue? */
> > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
> >  			  MSGQ__WRITE, &ad);
> >  	if (!rc)
> >  		/* Can this process send the message */
> > -		rc = avc_has_perm(tsec->sid, msec->sid,
> > +		rc = avc_has_perm(csec->action_sid, msec->sid,
> >  				  SECCLASS_MSG, MSG__SEND, &ad);
> >  	if (!rc)
> >  		/* Can the message be put in the queue? */
> > @@ -4237,10 +4284,10 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >   	ad.u.ipc_id = msq->q_perm.key;
> >  
> > -	rc = avc_has_perm(tsec->sid, isec->sid,
> > +	rc = avc_has_perm(tsec->victim_sid, isec->sid,
> >  			  SECCLASS_MSGQ, MSGQ__READ, &ad);
> >  	if (!rc)
> > -		rc = avc_has_perm(tsec->sid, msec->sid,
> > +		rc = avc_has_perm(tsec->victim_sid, msec->sid,
> >  				  SECCLASS_MSG, MSG__RECEIVE, &ad);
> >  	return rc;
> >  }
> > @@ -4248,7 +4295,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
> >  /* Shared Memory security operations */
> >  static int selinux_shm_alloc_security(struct shmid_kernel *shp)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  	int rc;
> > @@ -4257,13 +4304,13 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
> >  	if (rc)
> >  		return rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = shp->shm_perm.security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >   	ad.u.ipc_id = shp->shm_perm.key;
> >  
> > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
> > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
> >  			  SHM__CREATE, &ad);
> >  	if (rc) {
> >  		ipc_free_security(&shp->shm_perm);
> > @@ -4279,17 +4326,17 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
> >  
> >  static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = shp->shm_perm.security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >  	ad.u.ipc_id = shp->shm_perm.key;
> >  
> > -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
> > +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
> >  			    SHM__ASSOCIATE, &ad);
> >  }
> >  
> > @@ -4347,7 +4394,7 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
> >  /* Semaphore security operations */
> >  static int selinux_sem_alloc_security(struct sem_array *sma)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  	int rc;
> > @@ -4356,13 +4403,13 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
> >  	if (rc)
> >  		return rc;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = sma->sem_perm.security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >   	ad.u.ipc_id = sma->sem_perm.key;
> >  
> > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
> > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
> >  			  SEM__CREATE, &ad);
> >  	if (rc) {
> >  		ipc_free_security(&sma->sem_perm);
> > @@ -4378,17 +4425,17 @@ static void selinux_sem_free_security(struct sem_array *sma)
> >  
> >  static int selinux_sem_associate(struct sem_array *sma, int semflg)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct ipc_security_struct *isec;
> >  	struct avc_audit_data ad;
> >  
> > -	tsec = current->security;
> > +	csec = current->cred->security;
> >  	isec = sma->sem_perm.security;
> >  
> >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> >  	ad.u.ipc_id = sma->sem_perm.key;
> >  
> > -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
> > +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
> >  			    SEM__ASSOCIATE, &ad);
> >  }
> >  
> > @@ -4504,6 +4551,7 @@ static int selinux_getprocattr(struct task_struct *p,
> >  			       char *name, char **value)
> >  {
> >  	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	u32 sid;
> >  	int error;
> >  	unsigned len;
> > @@ -4515,22 +4563,25 @@ static int selinux_getprocattr(struct task_struct *p,
> >  	}
> >  
> >  	tsec = p->security;
> > +	rcu_read_lock();
> > +	csec = task_cred(p)->security;
> >  
> >  	if (!strcmp(name, "current"))
> > -		sid = tsec->sid;
> > +		sid = tsec->victim_sid;
> >  	else if (!strcmp(name, "prev"))
> >  		sid = tsec->osid;
> >  	else if (!strcmp(name, "exec"))
> >  		sid = tsec->exec_sid;
> >  	else if (!strcmp(name, "fscreate"))
> > -		sid = tsec->create_sid;
> > +		sid = csec->create_sid;
> >  	else if (!strcmp(name, "keycreate"))
> > -		sid = tsec->keycreate_sid;
> > +		sid = csec->keycreate_sid;
> >  	else if (!strcmp(name, "sockcreate"))
> > -		sid = tsec->sockcreate_sid;
> > +		sid = csec->sockcreate_sid;
> >  	else
> > -		return -EINVAL;
> > +		goto invalid;
> >  
> > +	rcu_read_unlock();
> >  	if (!sid)
> >  		return 0;
> >  
> > @@ -4538,13 +4589,20 @@ static int selinux_getprocattr(struct task_struct *p,
> >  	if (error)
> >  		return error;
> >  	return len;
> > +
> > +invalid:
> > +	rcu_read_unlock();
> > +	return -EINVAL;
> >  }
> >  
> >  static int selinux_setprocattr(struct task_struct *p,
> >  			       char *name, void *value, size_t size)
> >  {
> >  	struct task_security_struct *tsec;
> > -	u32 sid = 0;
> > +	struct cred_security_struct *csec;
> > +	struct av_decision avd;
> > +	struct cred *cred;
> > +	u32 sid = 0, perm;
> >  	int error;
> >  	char *str = value;
> >  
> > @@ -4560,17 +4618,19 @@ static int selinux_setprocattr(struct task_struct *p,
> >  	 * above restriction is ever removed.
> >  	 */
> >  	if (!strcmp(name, "exec"))
> > -		error = task_has_perm(current, p, PROCESS__SETEXEC);
> > +		perm = PROCESS__SETEXEC;
> >  	else if (!strcmp(name, "fscreate"))
> > -		error = task_has_perm(current, p, PROCESS__SETFSCREATE);
> > +		perm = PROCESS__SETFSCREATE;
> >  	else if (!strcmp(name, "keycreate"))
> > -		error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
> > +		perm = PROCESS__SETKEYCREATE;
> >  	else if (!strcmp(name, "sockcreate"))
> > -		error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
> > +		perm = PROCESS__SETSOCKCREATE;
> >  	else if (!strcmp(name, "current"))
> > -		error = task_has_perm(current, p, PROCESS__SETCURRENT);
> > +		perm = PROCESS__SETCURRENT;
> >  	else
> > -		error = -EINVAL;
> > +		return -EINVAL;
> > +
> > +	error = task_has_perm(current, p, perm);
> >  	if (error)
> >  		return error;
> >  
> > @@ -4592,20 +4652,37 @@ static int selinux_setprocattr(struct task_struct *p,
> >  	   checks and may_create for the file creation checks. The
> >  	   operation will then fail if the context is not permitted. */
> >  	tsec = p->security;
> > -	if (!strcmp(name, "exec"))
> > +	csec = p->cred->security;
> > +	switch (perm) {
> > +	case PROCESS__SETEXEC:
> >  		tsec->exec_sid = sid;
> > -	else if (!strcmp(name, "fscreate"))
> > -		tsec->create_sid = sid;
> > -	else if (!strcmp(name, "keycreate")) {
> > +		break;
> > +
> > +	case PROCESS__SETKEYCREATE:
> >  		error = may_create_key(sid, p);
> >  		if (error)
> >  			return error;
> > -		tsec->keycreate_sid = sid;
> > -	} else if (!strcmp(name, "sockcreate"))
> > -		tsec->sockcreate_sid = sid;
> > -	else if (!strcmp(name, "current")) {
> > -		struct av_decision avd;
> > +	case PROCESS__SETFSCREATE:
> > +	case PROCESS__SETSOCKCREATE:
> > +		cred = dup_cred(current->cred);
> > +		if (!cred)
> > +			return -ENOMEM;
> > +		csec = cred->security;
> > +		switch (perm) {
> > +		case PROCESS__SETKEYCREATE:
> > +			csec->keycreate_sid = sid;
> > +			break;
> > +		case PROCESS__SETFSCREATE:
> > +			csec->create_sid = sid;
> > +			break;
> > +		case PROCESS__SETSOCKCREATE:
> > +			csec->sockcreate_sid = sid;
> > +			break;
> > +		}
> > +		set_current_cred(cred);
> > +		break;
> >  
> > +	case PROCESS__SETCURRENT:
> >  		if (sid == 0)
> >  			return -EINVAL;
> >  
> > @@ -4624,11 +4701,16 @@ static int selinux_setprocattr(struct task_struct *p,
> >                  }
> >  
> >  		/* Check permissions for the transition. */
> > -		error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
> > +		error = avc_has_perm(csec->action_sid, sid, SECCLASS_PROCESS,
> >  		                     PROCESS__DYNTRANSITION, NULL);
> >  		if (error)
> >  			return error;
> >  
> > +		cred = dup_cred(current->cred);
> > +		if (!cred)
> > +			return -ENOMEM;
> > +		csec = cred->security;
> > +
> >  		/* Check for ptracing, and update the task SID if ok.
> >  		   Otherwise, leave SID unchanged and fail. */
> >  		task_lock(p);
> > @@ -4636,20 +4718,25 @@ static int selinux_setprocattr(struct task_struct *p,
> >  			error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
> >  						     SECCLASS_PROCESS,
> >  						     PROCESS__PTRACE, 0, &avd);
> > -			if (!error)
> > -				tsec->sid = sid;
> > +			if (!error) {
> > +				csec->action_sid = tsec->victim_sid = sid;
> > +			}
> >  			task_unlock(p);
> >  			avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
> >  				  PROCESS__PTRACE, &avd, error, NULL);
> > -			if (error)
> > +			if (error) {
> > +				put_cred(cred);
> >  				return error;
> > +			}
> >  		} else {
> > -			tsec->sid = sid;
> > +			csec->action_sid = tsec->victim_sid = sid;
> >  			task_unlock(p);
> >  		}
> > -	}
> > -	else
> > +		set_current_cred(cred);
> > +		break;
> > +	default:
> >  		return -EINVAL;
> > +	}
> >  
> >  	return size;
> >  }
> > @@ -4669,18 +4756,21 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
> >  static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
> >  			     unsigned long flags)
> >  {
> > -	struct task_security_struct *tsec = tsk->security;
> > +	struct cred_security_struct *csec;
> >  	struct key_security_struct *ksec;
> >  
> >  	ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
> >  	if (!ksec)
> >  		return -ENOMEM;
> >  
> > +	rcu_read_lock();
> > +	csec = task_cred(tsk)->security;
> >  	ksec->obj = k;
> > -	if (tsec->keycreate_sid)
> > -		ksec->sid = tsec->keycreate_sid;
> > +	if (csec->keycreate_sid)
> > +		ksec->sid = csec->keycreate_sid;
> >  	else
> > -		ksec->sid = tsec->sid;
> > +		ksec->sid = csec->action_sid;
> > +	rcu_read_unlock();
> >  	k->security = ksec;
> >  
> >  	return 0;
> > @@ -4695,17 +4785,13 @@ static void selinux_key_free(struct key *k)
> >  }
> >  
> >  static int selinux_key_permission(key_ref_t key_ref,
> > -			    struct task_struct *ctx,
> > -			    key_perm_t perm)
> > +				  struct task_struct *ctx,
> > +				  key_perm_t perm)
> >  {
> >  	struct key *key;
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  	struct key_security_struct *ksec;
> > -
> > -	key = key_ref_to_ptr(key_ref);
> > -
> > -	tsec = ctx->security;
> > -	ksec = key->security;
> > +	u32 action_sid;
> >  
> >  	/* if no specific permissions are requested, we skip the
> >  	   permission check. No serious, additional covert channels
> > @@ -4713,7 +4799,16 @@ static int selinux_key_permission(key_ref_t key_ref,
> >  	if (perm == 0)
> >  		return 0;
> >  
> > -	return avc_has_perm(tsec->sid, ksec->sid,
> > +	key = key_ref_to_ptr(key_ref);
> > +
> > +	rcu_read_lock();
> > +	csec = task_cred(ctx)->security;
> > +	action_sid = csec->action_sid;
> > +	rcu_read_unlock();
> > +
> > +	ksec = key->security;
> > +
> > +	return avc_has_perm(action_sid, ksec->sid,
> >  			    SECCLASS_KEY, perm, NULL);
> >  }
> >  
> > @@ -4788,6 +4883,9 @@ static struct security_operations selinux_ops = {
> >  	.file_send_sigiotask =		selinux_file_send_sigiotask,
> >  	.file_receive =			selinux_file_receive,
> >  
> > +	.cred_dup =			selinux_cred_dup,
> > +	.cred_destroy =			selinux_cred_destroy,
> > +
> >  	.task_create =			selinux_task_create,
> >  	.task_alloc_security =		selinux_task_alloc_security,
> >  	.task_free_security =		selinux_task_free_security,
> > @@ -4896,6 +4994,17 @@ static struct security_operations selinux_ops = {
> >  #endif
> >  };
> >  
> > +/*
> > + * initial security credentials
> > + * - attached to init_cred which is never released
> > + */
> > +static struct cred_security_struct init_cred_sec = {
> > +	.action_sid	= SECINITSID_KERNEL,
> > +	.create_sid	= SECINITSID_UNLABELED,
> > +	.keycreate_sid	= SECINITSID_UNLABELED,
> > +	.sockcreate_sid	= SECINITSID_UNLABELED,
> > +};
> > +
> >  static __init int selinux_init(void)
> >  {
> >  	struct task_security_struct *tsec;
> > @@ -4907,11 +5016,15 @@ static __init int selinux_init(void)
> >  
> >  	printk(KERN_INFO "SELinux:  Initializing.\n");
> >  
> > +	/* Set the security state for the initial credentials */
> > +	init_cred.security = &init_cred_sec;
> > +	BUG_ON(current->cred != &init_cred);
> > +
> >  	/* Set the security state for the initial task. */
> >  	if (task_alloc_security(current))
> >  		panic("SELinux:  Failed to initialize initial task.\n");
> >  	tsec = current->security;
> > -	tsec->osid = tsec->sid = SECINITSID_KERNEL;
> > +	tsec->osid = tsec->victim_sid = SECINITSID_KERNEL;
> >  
> >  	sel_inode_cache = kmem_cache_create("selinux_inode_security",
> >  					    sizeof(struct inode_security_struct),
> > diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> > index 91b88f0..a1dbc1c 100644
> > --- a/security/selinux/include/objsec.h
> > +++ b/security/selinux/include/objsec.h
> > @@ -27,14 +27,22 @@
> >  #include "flask.h"
> >  #include "avc.h"
> >  
> > +/*
> > + * the security parameters associated with the credentials record structure
> > + * (struct cred::security)
> > + */
> > +struct cred_security_struct {
> > +	u32	action_sid;	/* perform action as SID */
> > +	u32	create_sid;	/* filesystem object creation as SID */
> > +	u32	keycreate_sid;	/* key creation as SID */
> > +	u32	sockcreate_sid;	/* socket creation as SID */
> > +};
> > +
> >  struct task_security_struct {
> >  	struct task_struct *task;      /* back pointer to task object */
> >  	u32 osid;            /* SID prior to last execve */
> > -	u32 sid;             /* current SID */
> > +	u32 victim_sid;	     /* current SID affecting victimisation of this task */
> >  	u32 exec_sid;        /* exec SID */
> > -	u32 create_sid;      /* fscreate SID */
> > -	u32 keycreate_sid;   /* keycreate SID */
> > -	u32 sockcreate_sid;  /* fscreate SID */
> >  	u32 ptrace_sid;      /* SID of ptrace parent */
> >  };
> >  
> > diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> > index c9e92da..9c6737f 100644
> > --- a/security/selinux/selinuxfs.c
> > +++ b/security/selinux/selinuxfs.c
> > @@ -77,13 +77,13 @@ extern void selnl_notify_setenforce(int val);
> >  static int task_has_security(struct task_struct *tsk,
> >  			     u32 perms)
> >  {
> > -	struct task_security_struct *tsec;
> > +	struct cred_security_struct *csec;
> >  
> > -	tsec = tsk->security;
> > -	if (!tsec)
> > +	csec = tsk->cred->security;
> > +	if (!csec)
> >  		return -EACCES;
> >  
> > -	return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
> > +	return avc_has_perm(csec->action_sid, SECINITSID_SECURITY,
> >  			    SECCLASS_SECURITY, perms, NULL);
> >  }
> >  
> > diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
> > index ba715f4..902d302 100644
> > --- a/security/selinux/xfrm.c
> > +++ b/security/selinux/xfrm.c
> > @@ -240,7 +240,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
> >  	/*
> >  	 * Does the subject have permission to set security context?
> >  	 */
> > -	rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> > +	rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
> >  			  SECCLASS_ASSOCIATION,
> >  			  ASSOCIATION__SETCONTEXT, NULL);
> >  	if (rc)
> > @@ -341,7 +341,7 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
> >  	int rc = 0;
> >  
> >  	if (ctx)
> > -		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> > +		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
> >  				  SECCLASS_ASSOCIATION,
> >  				  ASSOCIATION__SETCONTEXT, NULL);
> >  
> > @@ -383,7 +383,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
> >  	int rc = 0;
> >  
> >  	if (ctx)
> > -		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> > +		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
> >  				  SECCLASS_ASSOCIATION,
> >  				  ASSOCIATION__SETCONTEXT, NULL);
> >  
> > 
> > -
> > To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> -
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
-- 
Stephen Smalley
National Security Agency


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-24 14:21     ` Stephen Smalley
@ 2007-09-24 15:35       ` Serge E. Hallyn
  2007-09-24 15:52       ` David Howells
  1 sibling, 0 replies; 22+ messages in thread
From: Serge E. Hallyn @ 2007-09-24 15:35 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Serge E. Hallyn, David Howells, viro, hch, Trond.Myklebust,
	casey, linux-kernel, selinux, linux-security-module

Quoting Stephen Smalley (sds@tycho.nsa.gov):
> On Mon, 2007-09-24 at 09:00 -0500, Serge E. Hallyn wrote:
> > Quoting David Howells (dhowells@redhat.com):
> > > Move into the cred struct the part of the task security data that defines how a
> > > task acts upon an object.  The part that defines how something acts upon a task
> > > remains attached to the task.
> > > 
> > > For SELinux this requires some of task_security_struct to be split off into
> > > cred_security_struct which is then attached to struct cred.  Note that the
> > > contents of cred_security_struct may not be changed without the generation of a
> > > new struct cred.
> > > 
> > > The split is as follows:
> > > 
> > >  (*) create_sid, keycreate_sid and sockcreate_sid just move across.
> > > 
> > >  (*) sid is split into victim_sid - which remains - and action_sid - which
> > >      migrates.
> > 
> > My concern is with this victim_sid.  Whether the concern is valid
> > depends on exactly how the other credentials can be used, which isn't
> > yet entirely clear to me.
> > 
> > So my concern is that while a task is acting with alternate creds,
> > another task can act upon it based upon victim_sid.  So does this
> > open up the possibility for an unprivileged task to ptrace or kill
> > a task in the middle of a privileged operation?  Is that somehow
> > safe in the way this is used here?
> 
> I think that's why they want it - so that things like nfsd and
> cachefiles can switch the credentials used for permission checking and
> object labeling (actor_sid, fscreate_sid) without exposing them to
> access by other tasks via signals, ptrace, etc (victim_sid).  Similar to
> fsuid vs. uid.  And then the separate issue of migrating the permission
> checking and object labeling state into a separate credential structure
> that can have a separate lifecycle from the task.

Ah, ok, so the daemon would use this to act under the user's
credentials.  I was thinking the user would be using this to act
under the daemon's or kernel's sid.

Between that and David's response, that this is only for the duration of
one syscall (IIUC), and not exported to userspace, it sounds safe
enough at the moment.  I do worry about the fact that inevitably someone
will want to 'expand' on that  :)

My worry arose from the fact that I don't see
security_cred_kernel_act_as() being called anywhere in this patchset...

thanks,
-serge

> Precisely when to use one identity vs. the other though isn't always
> clear, and the potential for accidental divergence is also a concern.
> 
> I think it is a mistake to have selinux_get_task_sid() blindly return
> the victim SID btw.  I think you likely need to split that interface and
> require the caller to indicate what it wants, so that there is no
> accidental misuse of the victim SID where the caller wanted the actor
> SID.  Or at least rename that interface to make it clear that it only
> returns the victim SID.
> 
> > I guess I need to look more at the actual nfs patches etc.
> > 
> > thanks,
> > -serge
> > 
> > 
> > >  (*) osid, exec_sid and ptrace_sid remain.
> > > 
> > > victim_sid is the SID used to govern actions upon the task.  action_sid is used
> > > to govern actions made by the task.
> > > 
> > > When accessing the cred_security_struct of another process, RCU read procedures
> > > must be observed.
> > > 
> > > Signed-off-by: David Howells <dhowells@redhat.com>
> > > ---
> > > 
> > >  include/linux/cred.h              |    1 
> > >  include/linux/security.h          |   34 +++
> > >  kernel/cred.c                     |    7 +
> > >  security/dummy.c                  |   11 +
> > >  security/selinux/exports.c        |    6 
> > >  security/selinux/hooks.c          |  497 +++++++++++++++++++++++--------------
> > >  security/selinux/include/objsec.h |   16 +
> > >  security/selinux/selinuxfs.c      |    8 -
> > >  security/selinux/xfrm.c           |    6 
> > >  9 files changed, 380 insertions(+), 206 deletions(-)
> > > 
> > > diff --git a/include/linux/cred.h b/include/linux/cred.h
> > > index 22ae610..6c6feec 100644
> > > --- a/include/linux/cred.h
> > > +++ b/include/linux/cred.h
> > > @@ -26,6 +26,7 @@ struct cred {
> > >  	gid_t			gid;		/* fsgid as was */
> > >  	struct rcu_head		exterminate;	/* cred destroyer */
> > >  	struct group_info	*group_info;
> > > +	void			*security;
> > >  
> > >  	/* caches for references to the three task keyrings
> > >  	 * - note that key_ref_t isn't typedef'd at this point, hence the odd
> > > diff --git a/include/linux/security.h b/include/linux/security.h
> > > index 1a15526..e5ed2ea 100644
> > > --- a/include/linux/security.h
> > > +++ b/include/linux/security.h
> > > @@ -504,6 +504,18 @@ struct request_sock;
> > >   *	@file contains the file structure being received.
> > >   *	Return 0 if permission is granted.
> > >   *
> > > + * Security hooks for credential structure operations.
> > > + *
> > > + * @cred_dup:
> > > + *	Duplicate the credentials onto a duplicated cred structure.
> > > + *	@cred points to the credentials structure.  cred->security points to the
> > > + *	security struct that was attached to the original cred struct, but it
> > > + *	lacks a reference for the duplication if reference counting is needed.
> > > + *
> > > + * @cred_destroy:
> > > + *	Destroy the credentials attached to a cred structure.
> > > + *	@cred points to the credentials structure that is to be destroyed.
> > > + *
> > >   * Security hooks for task operations.
> > >   *
> > >   * @task_create:
> > > @@ -1257,6 +1269,9 @@ struct security_operations {
> > >  				    struct fown_struct * fown, int sig);
> > >  	int (*file_receive) (struct file * file);
> > >  
> > > +	int (*cred_dup)(struct cred *cred);
> > > +	void (*cred_destroy)(struct cred *cred);
> > > +
> > >  	int (*task_create) (unsigned long clone_flags);
> > >  	int (*task_alloc_security) (struct task_struct * p);
> > >  	void (*task_free_security) (struct task_struct * p);
> > > @@ -1864,6 +1879,16 @@ static inline int security_file_receive (struct file *file)
> > >  	return security_ops->file_receive (file);
> > >  }
> > >  
> > > +static inline int security_cred_dup(struct cred *cred)
> > > +{
> > > +	return security_ops->cred_dup(cred);
> > > +}
> > > +
> > > +static inline void security_cred_destroy(struct cred *cred)
> > > +{
> > > +	return security_ops->cred_destroy(cred);
> > > +}
> > > +
> > >  static inline int security_task_create (unsigned long clone_flags)
> > >  {
> > >  	return security_ops->task_create (clone_flags);
> > > @@ -2546,6 +2571,15 @@ static inline int security_file_receive (struct file *file)
> > >  	return 0;
> > >  }
> > >  
> > > +static inline int security_cred_dup(struct cred *cred)
> > > +{
> > > +	return 0;
> > > +}
> > > +
> > > +static inline void security_cred_destroy(struct cred *cred)
> > > +{
> > > +}
> > > +
> > >  static inline int security_task_create (unsigned long clone_flags)
> > >  {
> > >  	return 0;
> > > diff --git a/kernel/cred.c b/kernel/cred.c
> > > index e96dafe..6a9dda2 100644
> > > --- a/kernel/cred.c
> > > +++ b/kernel/cred.c
> > > @@ -92,6 +92,12 @@ struct cred *dup_cred(const struct cred *pcred)
> > >  	if (likely(cred)) {
> > >  		*cred = *pcred;
> > >  		atomic_set(&cred->usage, 1);
> > > +
> > > +		if (security_cred_dup(cred) < 0) {
> > > +			kfree(cred);
> > > +			return NULL;
> > > +		}
> > > +
> > >  		get_group_info(cred->group_info);
> > >  		key_get(key_ref_to_ptr(cred->session_keyring));
> > >  		key_get(key_ref_to_ptr(cred->process_keyring));
> > > @@ -109,6 +115,7 @@ static void put_cred_rcu(struct rcu_head *rcu)
> > >  {
> > >  	struct cred *cred = container_of(rcu, struct cred, exterminate);
> > >  
> > > +	security_cred_destroy(cred);
> > >  	put_group_info(cred->group_info);
> > >  	key_ref_put(cred->session_keyring);
> > >  	key_ref_put(cred->process_keyring);
> > > diff --git a/security/dummy.c b/security/dummy.c
> > > index 62de89c..f535cc6 100644
> > > --- a/security/dummy.c
> > > +++ b/security/dummy.c
> > > @@ -468,6 +468,15 @@ static int dummy_file_receive (struct file *file)
> > >  	return 0;
> > >  }
> > >  
> > > +static int dummy_cred_dup(struct cred *cred)
> > > +{
> > > +	return 0;
> > > +}
> > > +
> > > +static void dummy_cred_destroy(struct cred *cred)
> > > +{
> > > +}
> > > +
> > >  static int dummy_task_create (unsigned long clone_flags)
> > >  {
> > >  	return 0;
> > > @@ -1038,6 +1047,8 @@ void security_fixup_ops (struct security_operations *ops)
> > >  	set_to_dummy_if_null(ops, file_set_fowner);
> > >  	set_to_dummy_if_null(ops, file_send_sigiotask);
> > >  	set_to_dummy_if_null(ops, file_receive);
> > > +	set_to_dummy_if_null(ops, cred_dup);
> > > +	set_to_dummy_if_null(ops, cred_destroy);
> > >  	set_to_dummy_if_null(ops, task_create);
> > >  	set_to_dummy_if_null(ops, task_alloc_security);
> > >  	set_to_dummy_if_null(ops, task_free_security);
> > > diff --git a/security/selinux/exports.c b/security/selinux/exports.c
> > > index b6f9694..29cb87a 100644
> > > --- a/security/selinux/exports.c
> > > +++ b/security/selinux/exports.c
> > > @@ -57,7 +57,7 @@ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid)
> > >  {
> > >  	if (selinux_enabled) {
> > >  		struct task_security_struct *tsec = tsk->security;
> > > -		*sid = tsec->sid;
> > > +		*sid = tsec->victim_sid;
> > >  		return;
> > >  	}
> > >  	*sid = 0;
> > > @@ -77,9 +77,9 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
> > >  int selinux_relabel_packet_permission(u32 sid)
> > >  {
> > >  	if (selinux_enabled) {
> > > -		struct task_security_struct *tsec = current->security;
> > > +		struct cred_security_struct *csec = current->cred->security;
> > >  
> > > -		return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
> > > +		return avc_has_perm(csec->action_sid, sid, SECCLASS_PACKET,
> > >  				    PACKET__RELABELTO, NULL);
> > >  	}
> > >  	return 0;
> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > index 3694662..6fc41da 100644
> > > --- a/security/selinux/hooks.c
> > > +++ b/security/selinux/hooks.c
> > > @@ -162,7 +162,8 @@ static int task_alloc_security(struct task_struct *task)
> > >  		return -ENOMEM;
> > >  
> > >  	tsec->task = task;
> > > -	tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
> > > +	tsec->osid = tsec->victim_sid = tsec->ptrace_sid =
> > > +		SECINITSID_UNLABELED;
> > >  	task->security = tsec;
> > >  
> > >  	return 0;
> > > @@ -177,7 +178,7 @@ static void task_free_security(struct task_struct *task)
> > >  
> > >  static int inode_alloc_security(struct inode *inode)
> > >  {
> > > -	struct task_security_struct *tsec = current->security;
> > > +	struct cred_security_struct *csec = current->cred->security;
> > >  	struct inode_security_struct *isec;
> > >  
> > >  	isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL);
> > > @@ -189,7 +190,7 @@ static int inode_alloc_security(struct inode *inode)
> > >  	isec->inode = inode;
> > >  	isec->sid = SECINITSID_UNLABELED;
> > >  	isec->sclass = SECCLASS_FILE;
> > > -	isec->task_sid = tsec->sid;
> > > +	isec->task_sid = csec->action_sid;
> > >  	inode->i_security = isec;
> > >  
> > >  	return 0;
> > > @@ -211,7 +212,7 @@ static void inode_free_security(struct inode *inode)
> > >  
> > >  static int file_alloc_security(struct file *file)
> > >  {
> > > -	struct task_security_struct *tsec = current->security;
> > > +	struct cred_security_struct *csec = current->cred->security;
> > >  	struct file_security_struct *fsec;
> > >  
> > >  	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
> > > @@ -219,8 +220,8 @@ static int file_alloc_security(struct file *file)
> > >  		return -ENOMEM;
> > >  
> > >  	fsec->file = file;
> > > -	fsec->sid = tsec->sid;
> > > -	fsec->fown_sid = tsec->sid;
> > > +	fsec->sid = csec->action_sid;
> > > +	fsec->fown_sid = csec->action_sid;
> > >  	file->f_security = fsec;
> > >  
> > >  	return 0;
> > > @@ -333,26 +334,26 @@ static match_table_t tokens = {
> > >  
> > >  static int may_context_mount_sb_relabel(u32 sid,
> > >  			struct superblock_security_struct *sbsec,
> > > -			struct task_security_struct *tsec)
> > > +			struct cred_security_struct *csec)
> > >  {
> > >  	int rc;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > > +	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > >  			  FILESYSTEM__RELABELFROM, NULL);
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
> > > +	rc = avc_has_perm(csec->action_sid, sid, SECCLASS_FILESYSTEM,
> > >  			  FILESYSTEM__RELABELTO, NULL);
> > >  	return rc;
> > >  }
> > >  
> > >  static int may_context_mount_inode_relabel(u32 sid,
> > >  			struct superblock_security_struct *sbsec,
> > > -			struct task_security_struct *tsec)
> > > +			struct cred_security_struct *csec)
> > >  {
> > >  	int rc;
> > > -	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > > +	rc = avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > >  			  FILESYSTEM__RELABELFROM, NULL);
> > >  	if (rc)
> > >  		return rc;
> > > @@ -369,7 +370,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> > >  	const char *name;
> > >  	u32 sid;
> > >  	int alloc = 0, rc = 0, seen = 0;
> > > -	struct task_security_struct *tsec = current->security;
> > > +	struct cred_security_struct *csec = current->cred->security;
> > >  	struct superblock_security_struct *sbsec = sb->s_security;
> > >  
> > >  	if (!data)
> > > @@ -501,7 +502,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> > >  			goto out_free;
> > >  		}
> > >  
> > > -		rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> > > +		rc = may_context_mount_sb_relabel(sid, sbsec, csec);
> > >  		if (rc)
> > >  			goto out_free;
> > >  
> > > @@ -523,12 +524,12 @@ static int try_context_mount(struct super_block *sb, void *data)
> > >  		}
> > >  
> > >  		if (!fscontext) {
> > > -			rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> > > +			rc = may_context_mount_sb_relabel(sid, sbsec, csec);
> > >  			if (rc)
> > >  				goto out_free;
> > >  			sbsec->sid = sid;
> > >  		} else {
> > > -			rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> > > +			rc = may_context_mount_inode_relabel(sid, sbsec, csec);
> > >  			if (rc)
> > >  				goto out_free;
> > >  		}
> > > @@ -548,7 +549,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> > >  			goto out_free;
> > >  		}
> > >  
> > > -		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> > > +		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
> > >  		if (rc)
> > >  			goto out_free;
> > >  
> > > @@ -568,7 +569,7 @@ static int try_context_mount(struct super_block *sb, void *data)
> > >  		if (sid == sbsec->def_sid)
> > >  			goto out_free;
> > >  
> > > -		rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> > > +		rc = may_context_mount_inode_relabel(sid, sbsec, csec);
> > >  		if (rc)
> > >  			goto out_free;
> > >  
> > > @@ -1023,15 +1024,22 @@ static inline u32 signal_to_av(int sig)
> > >  
> > >  /* Check permission betweeen a pair of tasks, e.g. signal checks,
> > >     fork check, ptrace check, etc. */
> > > -static int task_has_perm(struct task_struct *tsk1,
> > > -			 struct task_struct *tsk2,
> > > +static int task_has_perm(struct task_struct *actor,
> > > +			 struct task_struct *victim,
> > >  			 u32 perms)
> > >  {
> > > -	struct task_security_struct *tsec1, *tsec2;
> > > +	struct cred_security_struct *csec;
> > > +	struct task_security_struct *tsec;
> > > +	u32 action_sid;
> > > +
> > > +	/* the actor may not be the current task */
> > > +	rcu_read_lock();
> > > +	csec = task_cred(actor)->security;
> > > +	action_sid = csec->action_sid;
> > > +	rcu_read_unlock();
> > >  
> > > -	tsec1 = tsk1->security;
> > > -	tsec2 = tsk2->security;
> > > -	return avc_has_perm(tsec1->sid, tsec2->sid,
> > > +	tsec = victim->security;
> > > +	return avc_has_perm(action_sid, tsec->victim_sid,
> > >  			    SECCLASS_PROCESS, perms, NULL);
> > >  }
> > >  
> > > @@ -1039,16 +1047,16 @@ static int task_has_perm(struct task_struct *tsk1,
> > >  static int task_has_capability(struct task_struct *tsk,
> > >  			       int cap)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct avc_audit_data ad;
> > >  
> > > -	tsec = tsk->security;
> > > +	csec = tsk->cred->security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad,CAP);
> > >  	ad.tsk = tsk;
> > >  	ad.u.cap = cap;
> > >  
> > > -	return avc_has_perm(tsec->sid, tsec->sid,
> > > +	return avc_has_perm(csec->action_sid, csec->action_sid,
> > >  			    SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
> > >  }
> > >  
> > > @@ -1056,11 +1064,11 @@ static int task_has_capability(struct task_struct *tsk,
> > >  static int task_has_system(struct task_struct *tsk,
> > >  			   u32 perms)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  
> > > -	tsec = tsk->security;
> > > +	csec = tsk->cred->security;
> > >  
> > > -	return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
> > > +	return avc_has_perm(csec->action_sid, SECINITSID_KERNEL,
> > >  			    SECCLASS_SYSTEM, perms, NULL);
> > >  }
> > >  
> > > @@ -1072,14 +1080,14 @@ static int inode_has_perm(struct task_struct *tsk,
> > >  			  u32 perms,
> > >  			  struct avc_audit_data *adp)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct inode_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  
> > >  	if (unlikely (IS_PRIVATE (inode)))
> > >  		return 0;
> > >  
> > > -	tsec = tsk->security;
> > > +	csec = tsk->cred->security;
> > >  	isec = inode->i_security;
> > >  
> > >  	if (!adp) {
> > > @@ -1088,7 +1096,8 @@ static int inode_has_perm(struct task_struct *tsk,
> > >  		ad.u.fs.inode = inode;
> > >  	}
> > >  
> > > -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
> > > +	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> > > +			    adp);
> > >  }
> > >  
> > >  /* Same as inode_has_perm, but pass explicit audit data containing
> > > @@ -1119,7 +1128,7 @@ static int file_has_perm(struct task_struct *tsk,
> > >  				struct file *file,
> > >  				u32 av)
> > >  {
> > > -	struct task_security_struct *tsec = tsk->security;
> > > +	struct cred_security_struct *csec = tsk->cred->security;
> > >  	struct file_security_struct *fsec = file->f_security;
> > >  	struct vfsmount *mnt = file->f_path.mnt;
> > >  	struct dentry *dentry = file->f_path.dentry;
> > > @@ -1131,8 +1140,8 @@ static int file_has_perm(struct task_struct *tsk,
> > >  	ad.u.fs.mnt = mnt;
> > >  	ad.u.fs.dentry = dentry;
> > >  
> > > -	if (tsec->sid != fsec->sid) {
> > > -		rc = avc_has_perm(tsec->sid, fsec->sid,
> > > +	if (csec->action_sid != fsec->sid) {
> > > +		rc = avc_has_perm(csec->action_sid, fsec->sid,
> > >  				  SECCLASS_FD,
> > >  				  FD__USE,
> > >  				  &ad);
> > > @@ -1152,36 +1161,36 @@ static int may_create(struct inode *dir,
> > >  		      struct dentry *dentry,
> > >  		      u16 tclass)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct inode_security_struct *dsec;
> > >  	struct superblock_security_struct *sbsec;
> > >  	u32 newsid;
> > >  	struct avc_audit_data ad;
> > >  	int rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	dsec = dir->i_security;
> > >  	sbsec = dir->i_sb->s_security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, FS);
> > >  	ad.u.fs.dentry = dentry;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
> > > +	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR,
> > >  			  DIR__ADD_NAME | DIR__SEARCH,
> > >  			  &ad);
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > > -		newsid = tsec->create_sid;
> > > +	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > > +		newsid = csec->create_sid;
> > >  	} else {
> > > -		rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
> > > -					     &newsid);
> > > +		rc = security_transition_sid(csec->action_sid, dsec->sid,
> > > +					     tclass, &newsid);
> > >  		if (rc)
> > >  			return rc;
> > >  	}
> > >  
> > > -	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
> > > +	rc = avc_has_perm(csec->action_sid, newsid, tclass, FILE__CREATE, &ad);
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > @@ -1194,11 +1203,12 @@ static int may_create(struct inode *dir,
> > >  static int may_create_key(u32 ksid,
> > >  			  struct task_struct *ctx)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  
> > > -	tsec = ctx->security;
> > > +	csec = ctx->cred->security;
> > >  
> > > -	return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
> > > +	return avc_has_perm(csec->action_sid, ksid, SECCLASS_KEY, KEY__CREATE,
> > > +			    NULL);
> > >  }
> > >  
> > >  #define MAY_LINK   0
> > > @@ -1211,13 +1221,13 @@ static int may_link(struct inode *dir,
> > >  		    int kind)
> > >  
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct inode_security_struct *dsec, *isec;
> > >  	struct avc_audit_data ad;
> > >  	u32 av;
> > >  	int rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	dsec = dir->i_security;
> > >  	isec = dentry->d_inode->i_security;
> > >  
> > > @@ -1226,7 +1236,7 @@ static int may_link(struct inode *dir,
> > >  
> > >  	av = DIR__SEARCH;
> > >  	av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
> > > -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
> > > +	rc = avc_has_perm(csec->action_sid, dsec->sid, SECCLASS_DIR, av, &ad);
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > @@ -1245,7 +1255,7 @@ static int may_link(struct inode *dir,
> > >  		return 0;
> > >  	}
> > >  
> > > -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
> > > +	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, av, &ad);
> > >  	return rc;
> > >  }
> > >  
> > > @@ -1254,14 +1264,14 @@ static inline int may_rename(struct inode *old_dir,
> > >  			     struct inode *new_dir,
> > >  			     struct dentry *new_dentry)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
> > >  	struct avc_audit_data ad;
> > >  	u32 av;
> > >  	int old_is_dir, new_is_dir;
> > >  	int rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	old_dsec = old_dir->i_security;
> > >  	old_isec = old_dentry->d_inode->i_security;
> > >  	old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
> > > @@ -1270,16 +1280,16 @@ static inline int may_rename(struct inode *old_dir,
> > >  	AVC_AUDIT_DATA_INIT(&ad, FS);
> > >  
> > >  	ad.u.fs.dentry = old_dentry;
> > > -	rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
> > > +	rc = avc_has_perm(csec->action_sid, old_dsec->sid, SECCLASS_DIR,
> > >  			  DIR__REMOVE_NAME | DIR__SEARCH, &ad);
> > >  	if (rc)
> > >  		return rc;
> > > -	rc = avc_has_perm(tsec->sid, old_isec->sid,
> > > +	rc = avc_has_perm(csec->action_sid, old_isec->sid,
> > >  			  old_isec->sclass, FILE__RENAME, &ad);
> > >  	if (rc)
> > >  		return rc;
> > >  	if (old_is_dir && new_dir != old_dir) {
> > > -		rc = avc_has_perm(tsec->sid, old_isec->sid,
> > > +		rc = avc_has_perm(csec->action_sid, old_isec->sid,
> > >  				  old_isec->sclass, DIR__REPARENT, &ad);
> > >  		if (rc)
> > >  			return rc;
> > > @@ -1289,15 +1299,17 @@ static inline int may_rename(struct inode *old_dir,
> > >  	av = DIR__ADD_NAME | DIR__SEARCH;
> > >  	if (new_dentry->d_inode)
> > >  		av |= DIR__REMOVE_NAME;
> > > -	rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
> > > +	rc = avc_has_perm(csec->action_sid, new_dsec->sid, SECCLASS_DIR, av,
> > > +			  &ad);
> > >  	if (rc)
> > >  		return rc;
> > >  	if (new_dentry->d_inode) {
> > >  		new_isec = new_dentry->d_inode->i_security;
> > >  		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
> > > -		rc = avc_has_perm(tsec->sid, new_isec->sid,
> > > +		rc = avc_has_perm(csec->action_sid, new_isec->sid,
> > >  				  new_isec->sclass,
> > > -				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
> > > +				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
> > > +				  &ad);
> > >  		if (rc)
> > >  			return rc;
> > >  	}
> > > @@ -1311,12 +1323,12 @@ static int superblock_has_perm(struct task_struct *tsk,
> > >  			       u32 perms,
> > >  			       struct avc_audit_data *ad)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct superblock_security_struct *sbsec;
> > >  
> > > -	tsec = tsk->security;
> > > +	csec = tsk->cred->security;
> > >  	sbsec = sb->s_security;
> > > -	return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > > +	return avc_has_perm(csec->action_sid, sbsec->sid, SECCLASS_FILESYSTEM,
> > >  			    perms, ad);
> > >  }
> > >  
> > > @@ -1369,7 +1381,7 @@ static inline u32 file_to_av(struct file *file)
> > >  
> > >  static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
> > >  {
> > > -	struct task_security_struct *psec = parent->security;
> > > +	struct cred_security_struct *psec;
> > >  	struct task_security_struct *csec = child->security;
> > >  	int rc;
> > >  
> > > @@ -1379,8 +1391,12 @@ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
> > >  
> > >  	rc = task_has_perm(parent, child, PROCESS__PTRACE);
> > >  	/* Save the SID of the tracing process for later use in apply_creds. */
> > > -	if (!(child->ptrace & PT_PTRACED) && !rc)
> > > -		csec->ptrace_sid = psec->sid;
> > > +	if (!(child->ptrace & PT_PTRACED) && !rc) {
> > > +		rcu_read_lock();
> > > +		psec = task_cred(parent)->security;
> > > +		csec->ptrace_sid = psec->action_sid;
> > > +		rcu_read_unlock();
> > > +	}
> > >  	return rc;
> > >  }
> > >  
> > > @@ -1470,7 +1486,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> > >  {
> > >  	int error = 0;
> > >  	u32 av;
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	u32 tsid;
> > >  	int rc;
> > >  
> > > @@ -1478,7 +1494,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  
> > >  	rc = selinux_sysctl_get_sid(table, (op == 0001) ?
> > >  				    SECCLASS_DIR : SECCLASS_FILE, &tsid);
> > > @@ -1490,7 +1506,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> > >  	/* The op values are "defined" in sysctl.c, thereby creating
> > >  	 * a bad coupling between this module and sysctl.c */
> > >  	if(op == 001) {
> > > -		error = avc_has_perm(tsec->sid, tsid,
> > > +		error = avc_has_perm(csec->action_sid, tsid,
> > >  				     SECCLASS_DIR, DIR__SEARCH, NULL);
> > >  	} else {
> > >  		av = 0;
> > > @@ -1499,7 +1515,7 @@ static int selinux_sysctl(ctl_table *table, int op)
> > >  		if (op & 002)
> > >  			av |= FILE__WRITE;
> > >  		if (av)
> > > -			error = avc_has_perm(tsec->sid, tsid,
> > > +			error = avc_has_perm(csec->action_sid, tsid,
> > >  					     SECCLASS_FILE, av, NULL);
> > >          }
> > >  
> > > @@ -1587,11 +1603,11 @@ static int selinux_syslog(int type)
> > >  static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
> > >  {
> > >  	int rc, cap_sys_admin = 0;
> > > -	struct task_security_struct *tsec = current->security;
> > > +	struct cred_security_struct *csec = current->cred->security;
> > >  
> > >  	rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
> > >  	if (rc == 0)
> > > -		rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
> > > +		rc = avc_has_perm_noaudit(csec->action_sid, csec->action_sid,
> > >  					  SECCLASS_CAPABILITY,
> > >  					  CAP_TO_MASK(CAP_SYS_ADMIN),
> > >  					  0,
> > > @@ -1624,6 +1640,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
> > >  static int selinux_bprm_set_security(struct linux_binprm *bprm)
> > >  {
> > >  	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct inode *inode = bprm->file->f_path.dentry->d_inode;
> > >  	struct inode_security_struct *isec;
> > >  	struct bprm_security_struct *bsec;
> > > @@ -1641,15 +1658,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
> > >  		return 0;
> > >  
> > >  	tsec = current->security;
> > > +	csec = bprm->cred->security;
> > >  	isec = inode->i_security;
> > >  
> > >  	/* Default to the current task SID. */
> > > -	bsec->sid = tsec->sid;
> > > +	bsec->sid = csec->action_sid;
> > >  
> > >  	/* Reset fs, key, and sock SIDs on execve. */
> > > -	tsec->create_sid = 0;
> > > -	tsec->keycreate_sid = 0;
> > > -	tsec->sockcreate_sid = 0;
> > > +	csec->create_sid = 0;
> > > +	csec->keycreate_sid = 0;
> > > +	csec->sockcreate_sid = 0;
> > >  
> > >  	if (tsec->exec_sid) {
> > >  		newsid = tsec->exec_sid;
> > > @@ -1657,7 +1675,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
> > >  		tsec->exec_sid = 0;
> > >  	} else {
> > >  		/* Check for a default transition on this program. */
> > > -		rc = security_transition_sid(tsec->sid, isec->sid,
> > > +		rc = security_transition_sid(csec->action_sid, isec->sid,
> > >  		                             SECCLASS_PROCESS, &newsid);
> > >  		if (rc)
> > >  			return rc;
> > > @@ -1668,16 +1686,16 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
> > >  	ad.u.fs.dentry = bprm->file->f_path.dentry;
> > >  
> > >  	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
> > > -		newsid = tsec->sid;
> > > +		newsid = csec->action_sid;
> > >  
> > > -        if (tsec->sid == newsid) {
> > > -		rc = avc_has_perm(tsec->sid, isec->sid,
> > > +        if (csec->action_sid == newsid) {
> > > +		rc = avc_has_perm(csec->action_sid, isec->sid,
> > >  				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
> > >  		if (rc)
> > >  			return rc;
> > >  	} else {
> > >  		/* Check permissions for the transition. */
> > > -		rc = avc_has_perm(tsec->sid, newsid,
> > > +		rc = avc_has_perm(csec->action_sid, newsid,
> > >  				  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
> > >  		if (rc)
> > >  			return rc;
> > > @@ -1709,11 +1727,11 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm)
> > >  	struct task_security_struct *tsec = current->security;
> > >  	int atsecure = 0;
> > >  
> > > -	if (tsec->osid != tsec->sid) {
> > > +	if (tsec->osid != tsec->victim_sid) {
> > >  		/* Enable secure mode for SIDs transitions unless
> > >  		   the noatsecure permission is granted between
> > >  		   the two SIDs, i.e. ahp returns 0. */
> > > -		atsecure = avc_has_perm(tsec->osid, tsec->sid,
> > > +		atsecure = avc_has_perm(tsec->osid, tsec->victim_sid,
> > >  					 SECCLASS_PROCESS,
> > >  					 PROCESS__NOATSECURE, NULL);
> > >  	}
> > > @@ -1823,6 +1841,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
> > >  static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
> > >  {
> > >  	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct bprm_security_struct *bsec;
> > >  	u32 sid;
> > >  	int rc;
> > > @@ -1830,17 +1849,17 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
> > >  	secondary_ops->bprm_apply_creds(bprm, unsafe);
> > >  
> > >  	tsec = current->security;
> > > -
> > > +	csec = bprm->cred->security;
> > >  	bsec = bprm->security;
> > >  	sid = bsec->sid;
> > >  
> > > -	tsec->osid = tsec->sid;
> > > +	tsec->osid = tsec->victim_sid;
> > >  	bsec->unsafe = 0;
> > > -	if (tsec->sid != sid) {
> > > +	if (tsec->victim_sid != sid) {
> > >  		/* Check for shared state.  If not ok, leave SID
> > >  		   unchanged and kill. */
> > >  		if (unsafe & LSM_UNSAFE_SHARE) {
> > > -			rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
> > > +			rc = avc_has_perm(tsec->victim_sid, sid, SECCLASS_PROCESS,
> > >  					PROCESS__SHARE, NULL);
> > >  			if (rc) {
> > >  				bsec->unsafe = 1;
> > > @@ -1859,7 +1878,9 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
> > >  				return;
> > >  			}
> > >  		}
> > > -		tsec->sid = sid;
> > > +		if (csec->action_sid == tsec->victim_sid)
> > > +			csec->action_sid = sid;
> > > +		tsec->victim_sid = sid;
> > >  	}
> > >  }
> > >  
> > > @@ -1881,7 +1902,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
> > >  		force_sig_specific(SIGKILL, current);
> > >  		return;
> > >  	}
> > > -	if (tsec->osid == tsec->sid)
> > > +	if (tsec->osid == tsec->victim_sid)
> > >  		return;
> > >  
> > >  	/* Close files for which the new task SID is not authorized. */
> > > @@ -1893,7 +1914,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
> > >  	   signals. This must occur _after_ the task SID has
> > >  	  been updated so that any kill done after the flush
> > >  	  will be checked against the new SID. */
> > > -	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
> > > +	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
> > >  			  PROCESS__SIGINH, NULL);
> > >  	if (rc) {
> > >  		memset(&itimer, 0, sizeof itimer);
> > > @@ -1920,7 +1941,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
> > >  	   than the default soft limit for cases where the default
> > >  	   is lower than the hard limit, e.g. RLIMIT_CORE or
> > >  	   RLIMIT_STACK.*/
> > > -	rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
> > > +	rc = avc_has_perm(tsec->osid, tsec->victim_sid, SECCLASS_PROCESS,
> > >  			  PROCESS__RLIMITINH, NULL);
> > >  	if (rc) {
> > >  		for (i = 0; i < RLIM_NLIMITS; i++) {
> > > @@ -2122,21 +2143,21 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
> > >  				       char **name, void **value,
> > >  				       size_t *len)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct inode_security_struct *dsec;
> > >  	struct superblock_security_struct *sbsec;
> > >  	u32 newsid, clen;
> > >  	int rc;
> > >  	char *namep = NULL, *context;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	dsec = dir->i_security;
> > >  	sbsec = dir->i_sb->s_security;
> > >  
> > > -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > > -		newsid = tsec->create_sid;
> > > +	if (csec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
> > > +		newsid = csec->create_sid;
> > >  	} else {
> > > -		rc = security_transition_sid(tsec->sid, dsec->sid,
> > > +		rc = security_transition_sid(csec->action_sid, dsec->sid,
> > >  					     inode_mode_to_security_class(inode->i_mode),
> > >  					     &newsid);
> > >  		if (rc) {
> > > @@ -2295,7 +2316,7 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
> > >  
> > >  static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
> > >  {
> > > -	struct task_security_struct *tsec = current->security;
> > > +	struct cred_security_struct *csec = current->cred->security;
> > >  	struct inode *inode = dentry->d_inode;
> > >  	struct inode_security_struct *isec = inode->i_security;
> > >  	struct superblock_security_struct *sbsec;
> > > @@ -2327,7 +2348,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
> > >  	AVC_AUDIT_DATA_INIT(&ad,FS);
> > >  	ad.u.fs.dentry = dentry;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
> > > +	rc = avc_has_perm(csec->action_sid, isec->sid, isec->sclass,
> > >  			  FILE__RELABELFROM, &ad);
> > >  	if (rc)
> > >  		return rc;
> > > @@ -2336,12 +2357,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
> > > +	rc = avc_has_perm(csec->action_sid, newsid, isec->sclass,
> > >  			  FILE__RELABELTO, &ad);
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	rc = security_validate_transition(isec->sid, newsid, tsec->sid,
> > > +	rc = security_validate_transition(isec->sid, newsid, csec->action_sid,
> > >  	                                  isec->sclass);
> > >  	if (rc)
> > >  		return rc;
> > > @@ -2575,8 +2596,9 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
> > >  			     unsigned long prot, unsigned long flags,
> > >  			     unsigned long addr, unsigned long addr_only)
> > >  {
> > > +	struct cred_security_struct *csec = current->cred->security;
> > >  	int rc = 0;
> > > -	u32 sid = ((struct task_security_struct*)(current->security))->sid;
> > > +	u32 sid = csec->action_sid;
> > >  
> > >  	if (addr < mmap_min_addr)
> > >  		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
> > > @@ -2690,7 +2712,7 @@ static int selinux_file_set_fowner(struct file *file)
> > >  
> > >  	tsec = current->security;
> > >  	fsec = file->f_security;
> > > -	fsec->fown_sid = tsec->sid;
> > > +	fsec->fown_sid = tsec->victim_sid;
> > >  
> > >  	return 0;
> > >  }
> > > @@ -2714,7 +2736,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
> > >  	else
> > >  		perm = signal_to_av(signum);
> > >  
> > > -	return avc_has_perm(fsec->fown_sid, tsec->sid,
> > > +	return avc_has_perm(fsec->fown_sid, tsec->victim_sid,
> > >  			    SECCLASS_PROCESS, perm, NULL);
> > >  }
> > >  
> > > @@ -2723,6 +2745,31 @@ static int selinux_file_receive(struct file *file)
> > >  	return file_has_perm(current, file, file_to_av(file));
> > >  }
> > >  
> > > +/* credential security operations */
> > > +
> > > +/*
> > > + * duplicate the security information attached to a credentials record that is
> > > + * itself undergoing duplication
> > > + */
> > > +static int selinux_cred_dup(struct cred *cred)
> > > +{
> > > +	cred->security = kmemdup(cred->security,
> > > +				 sizeof(struct cred_security_struct),
> > > +				 GFP_KERNEL);
> > > +	return cred->security ? 0 : -ENOMEM;
> > > +}
> > > +
> > > +/*
> > > + * destroy the security information attached to a credentials record
> > > + * - this is done under RCU, and may not be associated with the task that set it
> > > + *   up
> > > + */
> > > +static void selinux_cred_destroy(struct cred *cred)
> > > +{
> > > +	kfree(cred->security);
> > > +}
> > > +
> > > +
> > >  /* task security operations */
> > >  
> > >  static int selinux_task_create(unsigned long clone_flags)
> > > @@ -2749,13 +2796,10 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
> > >  	tsec2 = tsk->security;
> > >  
> > >  	tsec2->osid = tsec1->osid;
> > > -	tsec2->sid = tsec1->sid;
> > > +	tsec2->victim_sid = tsec1->victim_sid;
> > >  
> > > -	/* Retain the exec, fs, key, and sock SIDs across fork */
> > > +	/* Retain the exec SID across fork */
> > >  	tsec2->exec_sid = tsec1->exec_sid;
> > > -	tsec2->create_sid = tsec1->create_sid;
> > > -	tsec2->keycreate_sid = tsec1->keycreate_sid;
> > > -	tsec2->sockcreate_sid = tsec1->sockcreate_sid;
> > >  
> > >  	/* Retain ptracer SID across fork, if any.
> > >  	   This will be reset by the ptrace hook upon any
> > > @@ -2893,7 +2937,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
> > >  		perm = signal_to_av(sig);
> > >  	tsec = p->security;
> > >  	if (secid)
> > > -		rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
> > > +		rc = avc_has_perm(secid, tsec->victim_sid,
> > > +				  SECCLASS_PROCESS, perm, NULL);
> > >  	else
> > >  		rc = task_has_perm(current, p, perm);
> > >  	return rc;
> > > @@ -2927,8 +2972,8 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
> > >  	secondary_ops->task_reparent_to_init(p);
> > >  
> > >  	tsec = p->security;
> > > -	tsec->osid = tsec->sid;
> > > -	tsec->sid = SECINITSID_KERNEL;
> > > +	tsec->osid = tsec->victim_sid;
> > > +	tsec->victim_sid = SECINITSID_KERNEL;
> > >  	return;
> > >  }
> > >  
> > > @@ -2938,7 +2983,7 @@ static void selinux_task_to_inode(struct task_struct *p,
> > >  	struct task_security_struct *tsec = p->security;
> > >  	struct inode_security_struct *isec = inode->i_security;
> > >  
> > > -	isec->sid = tsec->sid;
> > > +	isec->sid = tsec->victim_sid;
> > >  	isec->initialized = 1;
> > >  	return;
> > >  }
> > > @@ -3163,11 +3208,11 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
> > >  			   u32 perms)
> > >  {
> > >  	struct inode_security_struct *isec;
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct avc_audit_data ad;
> > >  	int err = 0;
> > >  
> > > -	tsec = task->security;
> > > +	csec = task->cred->security;
> > >  	isec = SOCK_INODE(sock)->i_security;
> > >  
> > >  	if (isec->sid == SECINITSID_KERNEL)
> > > @@ -3175,7 +3220,8 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad,NET);
> > >  	ad.u.net.sk = sock->sk;
> > > -	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
> > > +	err = avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> > > +			   &ad);
> > >  
> > >  out:
> > >  	return err;
> > > @@ -3185,15 +3231,15 @@ static int selinux_socket_create(int family, int type,
> > >  				 int protocol, int kern)
> > >  {
> > >  	int err = 0;
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	u32 newsid;
> > >  
> > >  	if (kern)
> > >  		goto out;
> > >  
> > > -	tsec = current->security;
> > > -	newsid = tsec->sockcreate_sid ? : tsec->sid;
> > > -	err = avc_has_perm(tsec->sid, newsid,
> > > +	csec = current->cred->security;
> > > +	newsid = csec->sockcreate_sid ? : csec->action_sid;
> > > +	err = avc_has_perm(csec->action_sid, newsid,
> > >  			   socket_type_to_security_class(family, type,
> > >  			   protocol), SOCKET__CREATE, NULL);
> > >  
> > > @@ -3206,14 +3252,14 @@ static int selinux_socket_post_create(struct socket *sock, int family,
> > >  {
> > >  	int err = 0;
> > >  	struct inode_security_struct *isec;
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct sk_security_struct *sksec;
> > >  	u32 newsid;
> > >  
> > >  	isec = SOCK_INODE(sock)->i_security;
> > >  
> > > -	tsec = current->security;
> > > -	newsid = tsec->sockcreate_sid ? : tsec->sid;
> > > +	csec = current->cred->security;
> > > +	newsid = csec->sockcreate_sid ? : csec->action_sid;
> > >  	isec->sclass = socket_type_to_security_class(family, type, protocol);
> > >  	isec->sid = kern ? SECINITSID_KERNEL : newsid;
> > >  	isec->initialized = 1;
> > > @@ -4027,7 +4073,7 @@ static int ipc_alloc_security(struct task_struct *task,
> > >  			      struct kern_ipc_perm *perm,
> > >  			      u16 sclass)
> > >  {
> > > -	struct task_security_struct *tsec = task->security;
> > > +	struct cred_security_struct *csec = task->cred->security;
> > >  	struct ipc_security_struct *isec;
> > >  
> > >  	isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
> > > @@ -4036,7 +4082,7 @@ static int ipc_alloc_security(struct task_struct *task,
> > >  
> > >  	isec->sclass = sclass;
> > >  	isec->ipc_perm = perm;
> > > -	isec->sid = tsec->sid;
> > > +	isec->sid = csec->action_sid;
> > >  	perm->security = isec;
> > >  
> > >  	return 0;
> > > @@ -4075,17 +4121,18 @@ static void msg_msg_free_security(struct msg_msg *msg)
> > >  static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
> > >  			u32 perms)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = ipc_perms->security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >  	ad.u.ipc_id = ipc_perms->key;
> > >  
> > > -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
> > > +	return avc_has_perm(csec->action_sid, isec->sid, isec->sclass, perms,
> > > +			    &ad);
> > >  }
> > >  
> > >  static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
> > > @@ -4101,7 +4148,7 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
> > >  /* message queue security operations */
> > >  static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  	int rc;
> > > @@ -4110,13 +4157,13 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = msq->q_perm.security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >   	ad.u.ipc_id = msq->q_perm.key;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> > > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
> > >  			  MSGQ__CREATE, &ad);
> > >  	if (rc) {
> > >  		ipc_free_security(&msq->q_perm);
> > > @@ -4132,17 +4179,17 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
> > >  
> > >  static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = msq->q_perm.security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >  	ad.u.ipc_id = msq->q_perm.key;
> > >  
> > > -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> > > +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
> > >  			    MSGQ__ASSOCIATE, &ad);
> > >  }
> > >  
> > > @@ -4176,13 +4223,13 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
> > >  
> > >  static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct msg_security_struct *msec;
> > >  	struct avc_audit_data ad;
> > >  	int rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = msq->q_perm.security;
> > >  	msec = msg->security;
> > >  
> > > @@ -4194,7 +4241,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
> > >  		 * Compute new sid based on current process and
> > >  		 * message queue this message will be stored in
> > >  		 */
> > > -		rc = security_transition_sid(tsec->sid,
> > > +		rc = security_transition_sid(csec->action_sid,
> > >  					     isec->sid,
> > >  					     SECCLASS_MSG,
> > >  					     &msec->sid);
> > > @@ -4206,11 +4253,11 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
> > >  	ad.u.ipc_id = msq->q_perm.key;
> > >  
> > >  	/* Can this process write to the queue? */
> > > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
> > > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_MSGQ,
> > >  			  MSGQ__WRITE, &ad);
> > >  	if (!rc)
> > >  		/* Can this process send the message */
> > > -		rc = avc_has_perm(tsec->sid, msec->sid,
> > > +		rc = avc_has_perm(csec->action_sid, msec->sid,
> > >  				  SECCLASS_MSG, MSG__SEND, &ad);
> > >  	if (!rc)
> > >  		/* Can the message be put in the queue? */
> > > @@ -4237,10 +4284,10 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >   	ad.u.ipc_id = msq->q_perm.key;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, isec->sid,
> > > +	rc = avc_has_perm(tsec->victim_sid, isec->sid,
> > >  			  SECCLASS_MSGQ, MSGQ__READ, &ad);
> > >  	if (!rc)
> > > -		rc = avc_has_perm(tsec->sid, msec->sid,
> > > +		rc = avc_has_perm(tsec->victim_sid, msec->sid,
> > >  				  SECCLASS_MSG, MSG__RECEIVE, &ad);
> > >  	return rc;
> > >  }
> > > @@ -4248,7 +4295,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
> > >  /* Shared Memory security operations */
> > >  static int selinux_shm_alloc_security(struct shmid_kernel *shp)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  	int rc;
> > > @@ -4257,13 +4304,13 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = shp->shm_perm.security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >   	ad.u.ipc_id = shp->shm_perm.key;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
> > > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
> > >  			  SHM__CREATE, &ad);
> > >  	if (rc) {
> > >  		ipc_free_security(&shp->shm_perm);
> > > @@ -4279,17 +4326,17 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
> > >  
> > >  static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = shp->shm_perm.security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >  	ad.u.ipc_id = shp->shm_perm.key;
> > >  
> > > -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
> > > +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SHM,
> > >  			    SHM__ASSOCIATE, &ad);
> > >  }
> > >  
> > > @@ -4347,7 +4394,7 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
> > >  /* Semaphore security operations */
> > >  static int selinux_sem_alloc_security(struct sem_array *sma)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  	int rc;
> > > @@ -4356,13 +4403,13 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
> > >  	if (rc)
> > >  		return rc;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = sma->sem_perm.security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >   	ad.u.ipc_id = sma->sem_perm.key;
> > >  
> > > -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
> > > +	rc = avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
> > >  			  SEM__CREATE, &ad);
> > >  	if (rc) {
> > >  		ipc_free_security(&sma->sem_perm);
> > > @@ -4378,17 +4425,17 @@ static void selinux_sem_free_security(struct sem_array *sma)
> > >  
> > >  static int selinux_sem_associate(struct sem_array *sma, int semflg)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct ipc_security_struct *isec;
> > >  	struct avc_audit_data ad;
> > >  
> > > -	tsec = current->security;
> > > +	csec = current->cred->security;
> > >  	isec = sma->sem_perm.security;
> > >  
> > >  	AVC_AUDIT_DATA_INIT(&ad, IPC);
> > >  	ad.u.ipc_id = sma->sem_perm.key;
> > >  
> > > -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
> > > +	return avc_has_perm(csec->action_sid, isec->sid, SECCLASS_SEM,
> > >  			    SEM__ASSOCIATE, &ad);
> > >  }
> > >  
> > > @@ -4504,6 +4551,7 @@ static int selinux_getprocattr(struct task_struct *p,
> > >  			       char *name, char **value)
> > >  {
> > >  	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	u32 sid;
> > >  	int error;
> > >  	unsigned len;
> > > @@ -4515,22 +4563,25 @@ static int selinux_getprocattr(struct task_struct *p,
> > >  	}
> > >  
> > >  	tsec = p->security;
> > > +	rcu_read_lock();
> > > +	csec = task_cred(p)->security;
> > >  
> > >  	if (!strcmp(name, "current"))
> > > -		sid = tsec->sid;
> > > +		sid = tsec->victim_sid;
> > >  	else if (!strcmp(name, "prev"))
> > >  		sid = tsec->osid;
> > >  	else if (!strcmp(name, "exec"))
> > >  		sid = tsec->exec_sid;
> > >  	else if (!strcmp(name, "fscreate"))
> > > -		sid = tsec->create_sid;
> > > +		sid = csec->create_sid;
> > >  	else if (!strcmp(name, "keycreate"))
> > > -		sid = tsec->keycreate_sid;
> > > +		sid = csec->keycreate_sid;
> > >  	else if (!strcmp(name, "sockcreate"))
> > > -		sid = tsec->sockcreate_sid;
> > > +		sid = csec->sockcreate_sid;
> > >  	else
> > > -		return -EINVAL;
> > > +		goto invalid;
> > >  
> > > +	rcu_read_unlock();
> > >  	if (!sid)
> > >  		return 0;
> > >  
> > > @@ -4538,13 +4589,20 @@ static int selinux_getprocattr(struct task_struct *p,
> > >  	if (error)
> > >  		return error;
> > >  	return len;
> > > +
> > > +invalid:
> > > +	rcu_read_unlock();
> > > +	return -EINVAL;
> > >  }
> > >  
> > >  static int selinux_setprocattr(struct task_struct *p,
> > >  			       char *name, void *value, size_t size)
> > >  {
> > >  	struct task_security_struct *tsec;
> > > -	u32 sid = 0;
> > > +	struct cred_security_struct *csec;
> > > +	struct av_decision avd;
> > > +	struct cred *cred;
> > > +	u32 sid = 0, perm;
> > >  	int error;
> > >  	char *str = value;
> > >  
> > > @@ -4560,17 +4618,19 @@ static int selinux_setprocattr(struct task_struct *p,
> > >  	 * above restriction is ever removed.
> > >  	 */
> > >  	if (!strcmp(name, "exec"))
> > > -		error = task_has_perm(current, p, PROCESS__SETEXEC);
> > > +		perm = PROCESS__SETEXEC;
> > >  	else if (!strcmp(name, "fscreate"))
> > > -		error = task_has_perm(current, p, PROCESS__SETFSCREATE);
> > > +		perm = PROCESS__SETFSCREATE;
> > >  	else if (!strcmp(name, "keycreate"))
> > > -		error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
> > > +		perm = PROCESS__SETKEYCREATE;
> > >  	else if (!strcmp(name, "sockcreate"))
> > > -		error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
> > > +		perm = PROCESS__SETSOCKCREATE;
> > >  	else if (!strcmp(name, "current"))
> > > -		error = task_has_perm(current, p, PROCESS__SETCURRENT);
> > > +		perm = PROCESS__SETCURRENT;
> > >  	else
> > > -		error = -EINVAL;
> > > +		return -EINVAL;
> > > +
> > > +	error = task_has_perm(current, p, perm);
> > >  	if (error)
> > >  		return error;
> > >  
> > > @@ -4592,20 +4652,37 @@ static int selinux_setprocattr(struct task_struct *p,
> > >  	   checks and may_create for the file creation checks. The
> > >  	   operation will then fail if the context is not permitted. */
> > >  	tsec = p->security;
> > > -	if (!strcmp(name, "exec"))
> > > +	csec = p->cred->security;
> > > +	switch (perm) {
> > > +	case PROCESS__SETEXEC:
> > >  		tsec->exec_sid = sid;
> > > -	else if (!strcmp(name, "fscreate"))
> > > -		tsec->create_sid = sid;
> > > -	else if (!strcmp(name, "keycreate")) {
> > > +		break;
> > > +
> > > +	case PROCESS__SETKEYCREATE:
> > >  		error = may_create_key(sid, p);
> > >  		if (error)
> > >  			return error;
> > > -		tsec->keycreate_sid = sid;
> > > -	} else if (!strcmp(name, "sockcreate"))
> > > -		tsec->sockcreate_sid = sid;
> > > -	else if (!strcmp(name, "current")) {
> > > -		struct av_decision avd;
> > > +	case PROCESS__SETFSCREATE:
> > > +	case PROCESS__SETSOCKCREATE:
> > > +		cred = dup_cred(current->cred);
> > > +		if (!cred)
> > > +			return -ENOMEM;
> > > +		csec = cred->security;
> > > +		switch (perm) {
> > > +		case PROCESS__SETKEYCREATE:
> > > +			csec->keycreate_sid = sid;
> > > +			break;
> > > +		case PROCESS__SETFSCREATE:
> > > +			csec->create_sid = sid;
> > > +			break;
> > > +		case PROCESS__SETSOCKCREATE:
> > > +			csec->sockcreate_sid = sid;
> > > +			break;
> > > +		}
> > > +		set_current_cred(cred);
> > > +		break;
> > >  
> > > +	case PROCESS__SETCURRENT:
> > >  		if (sid == 0)
> > >  			return -EINVAL;
> > >  
> > > @@ -4624,11 +4701,16 @@ static int selinux_setprocattr(struct task_struct *p,
> > >                  }
> > >  
> > >  		/* Check permissions for the transition. */
> > > -		error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
> > > +		error = avc_has_perm(csec->action_sid, sid, SECCLASS_PROCESS,
> > >  		                     PROCESS__DYNTRANSITION, NULL);
> > >  		if (error)
> > >  			return error;
> > >  
> > > +		cred = dup_cred(current->cred);
> > > +		if (!cred)
> > > +			return -ENOMEM;
> > > +		csec = cred->security;
> > > +
> > >  		/* Check for ptracing, and update the task SID if ok.
> > >  		   Otherwise, leave SID unchanged and fail. */
> > >  		task_lock(p);
> > > @@ -4636,20 +4718,25 @@ static int selinux_setprocattr(struct task_struct *p,
> > >  			error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
> > >  						     SECCLASS_PROCESS,
> > >  						     PROCESS__PTRACE, 0, &avd);
> > > -			if (!error)
> > > -				tsec->sid = sid;
> > > +			if (!error) {
> > > +				csec->action_sid = tsec->victim_sid = sid;
> > > +			}
> > >  			task_unlock(p);
> > >  			avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
> > >  				  PROCESS__PTRACE, &avd, error, NULL);
> > > -			if (error)
> > > +			if (error) {
> > > +				put_cred(cred);
> > >  				return error;
> > > +			}
> > >  		} else {
> > > -			tsec->sid = sid;
> > > +			csec->action_sid = tsec->victim_sid = sid;
> > >  			task_unlock(p);
> > >  		}
> > > -	}
> > > -	else
> > > +		set_current_cred(cred);
> > > +		break;
> > > +	default:
> > >  		return -EINVAL;
> > > +	}
> > >  
> > >  	return size;
> > >  }
> > > @@ -4669,18 +4756,21 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
> > >  static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
> > >  			     unsigned long flags)
> > >  {
> > > -	struct task_security_struct *tsec = tsk->security;
> > > +	struct cred_security_struct *csec;
> > >  	struct key_security_struct *ksec;
> > >  
> > >  	ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
> > >  	if (!ksec)
> > >  		return -ENOMEM;
> > >  
> > > +	rcu_read_lock();
> > > +	csec = task_cred(tsk)->security;
> > >  	ksec->obj = k;
> > > -	if (tsec->keycreate_sid)
> > > -		ksec->sid = tsec->keycreate_sid;
> > > +	if (csec->keycreate_sid)
> > > +		ksec->sid = csec->keycreate_sid;
> > >  	else
> > > -		ksec->sid = tsec->sid;
> > > +		ksec->sid = csec->action_sid;
> > > +	rcu_read_unlock();
> > >  	k->security = ksec;
> > >  
> > >  	return 0;
> > > @@ -4695,17 +4785,13 @@ static void selinux_key_free(struct key *k)
> > >  }
> > >  
> > >  static int selinux_key_permission(key_ref_t key_ref,
> > > -			    struct task_struct *ctx,
> > > -			    key_perm_t perm)
> > > +				  struct task_struct *ctx,
> > > +				  key_perm_t perm)
> > >  {
> > >  	struct key *key;
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  	struct key_security_struct *ksec;
> > > -
> > > -	key = key_ref_to_ptr(key_ref);
> > > -
> > > -	tsec = ctx->security;
> > > -	ksec = key->security;
> > > +	u32 action_sid;
> > >  
> > >  	/* if no specific permissions are requested, we skip the
> > >  	   permission check. No serious, additional covert channels
> > > @@ -4713,7 +4799,16 @@ static int selinux_key_permission(key_ref_t key_ref,
> > >  	if (perm == 0)
> > >  		return 0;
> > >  
> > > -	return avc_has_perm(tsec->sid, ksec->sid,
> > > +	key = key_ref_to_ptr(key_ref);
> > > +
> > > +	rcu_read_lock();
> > > +	csec = task_cred(ctx)->security;
> > > +	action_sid = csec->action_sid;
> > > +	rcu_read_unlock();
> > > +
> > > +	ksec = key->security;
> > > +
> > > +	return avc_has_perm(action_sid, ksec->sid,
> > >  			    SECCLASS_KEY, perm, NULL);
> > >  }
> > >  
> > > @@ -4788,6 +4883,9 @@ static struct security_operations selinux_ops = {
> > >  	.file_send_sigiotask =		selinux_file_send_sigiotask,
> > >  	.file_receive =			selinux_file_receive,
> > >  
> > > +	.cred_dup =			selinux_cred_dup,
> > > +	.cred_destroy =			selinux_cred_destroy,
> > > +
> > >  	.task_create =			selinux_task_create,
> > >  	.task_alloc_security =		selinux_task_alloc_security,
> > >  	.task_free_security =		selinux_task_free_security,
> > > @@ -4896,6 +4994,17 @@ static struct security_operations selinux_ops = {
> > >  #endif
> > >  };
> > >  
> > > +/*
> > > + * initial security credentials
> > > + * - attached to init_cred which is never released
> > > + */
> > > +static struct cred_security_struct init_cred_sec = {
> > > +	.action_sid	= SECINITSID_KERNEL,
> > > +	.create_sid	= SECINITSID_UNLABELED,
> > > +	.keycreate_sid	= SECINITSID_UNLABELED,
> > > +	.sockcreate_sid	= SECINITSID_UNLABELED,
> > > +};
> > > +
> > >  static __init int selinux_init(void)
> > >  {
> > >  	struct task_security_struct *tsec;
> > > @@ -4907,11 +5016,15 @@ static __init int selinux_init(void)
> > >  
> > >  	printk(KERN_INFO "SELinux:  Initializing.\n");
> > >  
> > > +	/* Set the security state for the initial credentials */
> > > +	init_cred.security = &init_cred_sec;
> > > +	BUG_ON(current->cred != &init_cred);
> > > +
> > >  	/* Set the security state for the initial task. */
> > >  	if (task_alloc_security(current))
> > >  		panic("SELinux:  Failed to initialize initial task.\n");
> > >  	tsec = current->security;
> > > -	tsec->osid = tsec->sid = SECINITSID_KERNEL;
> > > +	tsec->osid = tsec->victim_sid = SECINITSID_KERNEL;
> > >  
> > >  	sel_inode_cache = kmem_cache_create("selinux_inode_security",
> > >  					    sizeof(struct inode_security_struct),
> > > diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> > > index 91b88f0..a1dbc1c 100644
> > > --- a/security/selinux/include/objsec.h
> > > +++ b/security/selinux/include/objsec.h
> > > @@ -27,14 +27,22 @@
> > >  #include "flask.h"
> > >  #include "avc.h"
> > >  
> > > +/*
> > > + * the security parameters associated with the credentials record structure
> > > + * (struct cred::security)
> > > + */
> > > +struct cred_security_struct {
> > > +	u32	action_sid;	/* perform action as SID */
> > > +	u32	create_sid;	/* filesystem object creation as SID */
> > > +	u32	keycreate_sid;	/* key creation as SID */
> > > +	u32	sockcreate_sid;	/* socket creation as SID */
> > > +};
> > > +
> > >  struct task_security_struct {
> > >  	struct task_struct *task;      /* back pointer to task object */
> > >  	u32 osid;            /* SID prior to last execve */
> > > -	u32 sid;             /* current SID */
> > > +	u32 victim_sid;	     /* current SID affecting victimisation of this task */
> > >  	u32 exec_sid;        /* exec SID */
> > > -	u32 create_sid;      /* fscreate SID */
> > > -	u32 keycreate_sid;   /* keycreate SID */
> > > -	u32 sockcreate_sid;  /* fscreate SID */
> > >  	u32 ptrace_sid;      /* SID of ptrace parent */
> > >  };
> > >  
> > > diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> > > index c9e92da..9c6737f 100644
> > > --- a/security/selinux/selinuxfs.c
> > > +++ b/security/selinux/selinuxfs.c
> > > @@ -77,13 +77,13 @@ extern void selnl_notify_setenforce(int val);
> > >  static int task_has_security(struct task_struct *tsk,
> > >  			     u32 perms)
> > >  {
> > > -	struct task_security_struct *tsec;
> > > +	struct cred_security_struct *csec;
> > >  
> > > -	tsec = tsk->security;
> > > -	if (!tsec)
> > > +	csec = tsk->cred->security;
> > > +	if (!csec)
> > >  		return -EACCES;
> > >  
> > > -	return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
> > > +	return avc_has_perm(csec->action_sid, SECINITSID_SECURITY,
> > >  			    SECCLASS_SECURITY, perms, NULL);
> > >  }
> > >  
> > > diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
> > > index ba715f4..902d302 100644
> > > --- a/security/selinux/xfrm.c
> > > +++ b/security/selinux/xfrm.c
> > > @@ -240,7 +240,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
> > >  	/*
> > >  	 * Does the subject have permission to set security context?
> > >  	 */
> > > -	rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> > > +	rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
> > >  			  SECCLASS_ASSOCIATION,
> > >  			  ASSOCIATION__SETCONTEXT, NULL);
> > >  	if (rc)
> > > @@ -341,7 +341,7 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
> > >  	int rc = 0;
> > >  
> > >  	if (ctx)
> > > -		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> > > +		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
> > >  				  SECCLASS_ASSOCIATION,
> > >  				  ASSOCIATION__SETCONTEXT, NULL);
> > >  
> > > @@ -383,7 +383,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
> > >  	int rc = 0;
> > >  
> > >  	if (ctx)
> > > -		rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
> > > +		rc = avc_has_perm(tsec->action_sid, ctx->ctx_sid,
> > >  				  SECCLASS_ASSOCIATION,
> > >  				  ASSOCIATION__SETCONTEXT, NULL);
> > >  
> > > 
> > > -
> > > To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > -
> > To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> -- 
> Stephen Smalley
> National Security Agency

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-24 14:21     ` Stephen Smalley
  2007-09-24 15:35       ` Serge E. Hallyn
@ 2007-09-24 15:52       ` David Howells
  1 sibling, 0 replies; 22+ messages in thread
From: David Howells @ 2007-09-24 15:52 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: dhowells, Stephen Smalley, viro, hch, Trond.Myklebust, casey,
	linux-kernel, selinux, linux-security-module

Serge E. Hallyn <serge@hallyn.com> wrote:

> Ah, ok, so the daemon would use this to act under the user's
> credentials.  I was thinking the user would be using this to act
> under the daemon's or kernel's sid.

Think kernel service rather than daemon.  NFSd provides its own daemons to
override the security of, whereas cachefiles runs in the process context of
whoever called NFS.

> Between that and David's response, that this is only for the duration of
> one syscall (IIUC),

That's more or less correct.  You have to add the pagefault handler to that
list.

> My worry arose from the fact that I don't see
> security_cred_kernel_act_as() being called anywhere in this patchset...

Look in:

[PATCH 04/22] CRED: Request a credential record for a kernel service

Which was part of a patchset I sent on the 21st Sept.  get_kernel_cred() is in
turn used by:

[PATCH 13/22] CacheFiles: A cache that backs onto a mounted filesystem

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-24 14:00   ` Serge E. Hallyn
  2007-09-24 14:21     ` Stephen Smalley
@ 2007-09-26 13:30     ` David Howells
  2007-09-26 14:14       ` Stephen Smalley
  2007-09-26 14:58       ` Casey Schaufler
  1 sibling, 2 replies; 22+ messages in thread
From: David Howells @ 2007-09-26 13:30 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: dhowells, Serge E. Hallyn, viro, hch, Trond.Myklebust, casey,
	linux-kernel, selinux, linux-security-module

Stephen Smalley <sds@tycho.nsa.gov> wrote:

> Precisely when to use one identity vs. the other though isn't always
> clear, and the potential for accidental divergence is also a concern.

What should auditing use in audit_filter_rules() when dealing with
AUDIT_SUBJ_* cases?  Should the SUBJ cases use the subjective SID and the
AUDIT_OBJ_* cases use the objective SID?  On the other hand AUDIT_OBJ_* cases
don't seem to have anything to do with tasks.

David

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-26 13:30     ` David Howells
@ 2007-09-26 14:14       ` Stephen Smalley
  2007-09-26 14:58       ` Casey Schaufler
  1 sibling, 0 replies; 22+ messages in thread
From: Stephen Smalley @ 2007-09-26 14:14 UTC (permalink / raw)
  To: David Howells
  Cc: Serge E. Hallyn, viro, hch, Trond.Myklebust, casey, linux-kernel,
	selinux, linux-security-module, linux-audit

On Wed, 2007-09-26 at 14:30 +0100, David Howells wrote:
> Stephen Smalley <sds@tycho.nsa.gov> wrote:
> 
> > Precisely when to use one identity vs. the other though isn't always
> > clear, and the potential for accidental divergence is also a concern.
> 
> What should auditing use in audit_filter_rules() when dealing with
> AUDIT_SUBJ_* cases?  Should the SUBJ cases use the subjective SID and the
> AUDIT_OBJ_* cases use the objective SID?  On the other hand AUDIT_OBJ_* cases
> don't seem to have anything to do with tasks.

(cc'd linux-audit)

As you say, I don't think AUDIT_OBJ_* has anything to do with tasks,
just object labels (like inode labels).

I think you likely want the actor SID / subject SID or whatever you want
to call it for AUDIT_SUBJ_*.

-- 
Stephen Smalley
National Security Agency


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred
  2007-09-26 13:30     ` David Howells
  2007-09-26 14:14       ` Stephen Smalley
@ 2007-09-26 14:58       ` Casey Schaufler
  1 sibling, 0 replies; 22+ messages in thread
From: Casey Schaufler @ 2007-09-26 14:58 UTC (permalink / raw)
  To: David Howells, Stephen Smalley
  Cc: dhowells, Serge E. Hallyn, viro, hch, Trond.Myklebust, casey,
	linux-kernel, selinux, linux-security-module


--- David Howells <dhowells@redhat.com> wrote:

> Stephen Smalley <sds@tycho.nsa.gov> wrote:
> 
> > Precisely when to use one identity vs. the other though isn't always
> > clear, and the potential for accidental divergence is also a concern.
> 
> What should auditing use in audit_filter_rules() when dealing with
> AUDIT_SUBJ_* cases?  Should the SUBJ cases use the subjective SID and the
> AUDIT_OBJ_* cases use the objective SID?  On the other hand AUDIT_OBJ_* cases
> don't seem to have anything to do with tasks.

I believe that you'll need to audit both sets of credentials.
I think that for audit filtering you will need to have the ability
to filter on either. It's no different from the euid/ruid split.


Casey Schaufler
casey@schaufler-ca.com

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] CRED: Move the effective capabilities into the cred struct
  2007-09-20  4:11   ` Andrew Morgan
  2007-09-20 13:38     ` Trond Myklebust
@ 2007-09-26 18:23     ` Al Viro
  1 sibling, 0 replies; 22+ messages in thread
From: Al Viro @ 2007-09-26 18:23 UTC (permalink / raw)
  To: Andrew Morgan
  Cc: David Howells, hch, Trond.Myklebust, sds, casey, linux-kernel,
	selinux, linux-security-module

On Wed, Sep 19, 2007 at 09:11:26PM -0700, Andrew Morgan wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> David Howells wrote:
> > Move the effective capabilities mask from the task struct into the credentials
> > record.
> > 
> > Note that the effective capabilities mask in the cred struct shadows that in
> > the task_struct because a thread can have its capabilities masks changed by
> > another thread.  The shadowing is performed by update_current_cred() which is
> > invoked on entry to any system call that might need it.
> 
> OOC If we were to simply drop support for one process changing the
> capabilities of another, would we need this patch?

Umm...  It would become simpler (which is a damn good thing - less PITA
with update_current_cred), but it would be still needed.

FWIW, dropping that support would be a Good Thing(tm), as far as I'm
concerned.  _Why_ do we want that, anyway, and how much userland code
is able to cope with that in sane way?

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2007-09-26 18:24 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-09-19 16:17 [PATCH 0/3] Introduce credential record David Howells
2007-09-19 16:17 ` [PATCH 1/3] CRED: Introduce a COW credentials record David Howells
2007-09-19 16:18 ` [PATCH 2/3] CRED: Split the task security data and move part of it into struct cred David Howells
2007-09-19 17:28   ` Casey Schaufler
2007-09-19 22:57     ` David Howells
2007-09-20 16:31       ` Casey Schaufler
2007-09-20 17:17         ` David Howells
2007-09-20 17:33         ` David Howells
2007-09-24 14:00   ` Serge E. Hallyn
2007-09-24 14:21     ` Stephen Smalley
2007-09-24 15:35       ` Serge E. Hallyn
2007-09-24 15:52       ` David Howells
2007-09-26 13:30     ` David Howells
2007-09-26 14:14       ` Stephen Smalley
2007-09-26 14:58       ` Casey Schaufler
2007-09-19 16:18 ` [PATCH 3/3] CRED: Move the effective capabilities into the cred struct David Howells
2007-09-20  4:11   ` Andrew Morgan
2007-09-20 13:38     ` Trond Myklebust
2007-09-20 15:36       ` Casey Schaufler
2007-09-20 16:09         ` Trond Myklebust
2007-09-26 18:23     ` Al Viro
2007-09-20  8:15   ` David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).