[PATCH 07/37] Security: De-embed task security record from task and use refcounting

David Howells
2008-02-08T16:57:51Z

Remove the temporarily embedded task security record from task_struct. Instead
it is made to dangle from the task_struct::sec and task_struct::act_as pointers
with references counted for each.

do_coredump() is made to create a copy of the security record, modify it and
then use that to override the main one for a task. sys_faccessat() is made to
do the same.

The process and session keyrings are moved from signal_struct into a new
thread_group_security struct. This is then refcounted, with pointers coming
from the task_security struct instead of from signal_struct.

The keyring functions then take pointers to task_security structs rather than
task_structs for their security contexts. This is so that request_key() can
proceed asynchronously without having to worry about the initiator task's
act_as pointer changing.

The LSM hooks for dealing with task security are modified to deal with the task
security struct directly rather than going via the task_struct as appopriate.

This permits the subjective security context of a task to be overridden by
changing its act_as pointer without altering its objective security pointer,
and thus not breaking signalling, ptrace, etc. whilst the override is in force.

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

fs/exec.c | 15 +-
fs/open.c | 37 ++---
include/linux/init_task.h | 18 --
include/linux/key-ui.h | 10 +
include/linux/key.h | 31 +---
include/linux/sched.h | 40 ++++-
include/linux/security.h | 43 ++++--
kernel/Makefile | 2
kernel/cred.c | 140 ++++++++++++++++++
kernel/exit.c | 1
kernel/fork.c | 40 ++---
kernel/kmod.c | 10 +
kernel/sys.c | 16 +-
kernel/user.c | 2
net/rxrpc/ar-key.c | 4 -
security/dummy.c | 14 +-
security/keys/internal.h | 10 +
security/keys/key.c | 6 -
security/keys/keyctl.c | 6 -
security/keys/keyring.c | 14 +-
security/keys/permission.c | 5 -
security/keys/proc.c | 2
security/keys/process_keys.c | 290 +++++++++++++++++---------------------
security/keys/request_key.c | 59 ++++----
security/keys/request_key_auth.c | 38 ++---
security/security.c | 20 ++-
security/selinux/hooks.c | 46 +++++-
27 files changed, 527 insertions(+), 392 deletions(-)
create mode 100644 kernel/cred.c

diff --git a/fs/exec.c b/fs/exec.c
index c09a2d9..afbcc34 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1670,13 +1670,13 @@ int get_dumpable(struct mm_struct *mm)

int do_coredump(long signr, int exit_code, struct pt_regs * regs)
{
+ struct task_security *sec, *old_act_as;
char corename[CORENAME_MAX_SIZE + 1];
struct mm_struct *mm = current->mm;
struct linux_binfmt * binfmt;
struct inode * inode;
struct file * file;
int retval = 0;
- int fsuid = current_fsuid();
int flag = 0;
int ispipe = 0;
unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
@@ -1688,7 +1688,10 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)

binfmt = current->binfmt;
if (!binfmt || !binfmt->core_dump)
- goto fail;
+ goto fail_nosubj;
+ sec = dup_task_security(current->sec);
+ if (!sec)
+ goto fail_nosubj;
down_write(&mm->mmap_sem);
/*
* If another thread got here first, or we are not dumpable, bail out.
@@ -1703,9 +1706,11 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
* process nor do we know its entire history. We only know it
* was tainted so we dump it as root in mode 2.
*/
+ old_act_as = current->act_as;
if (get_dumpable(mm) == 2) { /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
- current->act_as->fsuid = 0; /* Dump root private */
+ sec->fsuid = 0; /* Dump root private */
+ current->act_as = sec;
}

retval = coredump_wait(exit_code);
@@ -1801,8 +1806,10 @@ fail_unlock:
if (helper_argv)
argv_free(helper_argv);

- current->act_as->fsuid = fsuid;
+ current->act_as = old_act_as;
complete_all(&mm->core_done);
fail:
+ put_task_security(sec);
+fail_nosubj:
return retval;
}
diff --git a/fs/open.c b/fs/open.c
index 6d7a29c..710102a 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -420,34 +420,27 @@ out:
*/
asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
{
+ struct task_security *sec, *old_act_as;
struct nameidata nd;
- int old_fsuid, old_fsgid;
- kernel_cap_t old_cap;
int res;

if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;

- old_fsuid = current->act_as->fsuid;
- old_fsgid = current->act_as->fsgid;
- old_cap = current->act_as->cap_effective;
+ sec = dup_task_security(current->sec);
+ if (!sec)
+ return -ENOMEM;
+ sec->fsuid = current->sec->uid;
+ sec->fsgid = current->sec->gid;

- current->act_as->fsuid = current->act_as->uid;
- current->act_as->fsgid = current->act_as->gid;
-
- /*
- * 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->act_as->uid)
- cap_clear(current->act_as->cap_effective);
+ /* Clear the capabilities if we switch to a non-root user */
+ if (current->sec->uid)
+ cap_clear(sec->cap_effective);
else
- current->act_as->cap_effective = current->act_as->cap_permitted;
+ sec->cap_effective = current->sec->cap_permitted;

+ old_act_as = current->act_as;
+ current->act_as = sec;
res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
if (res)
goto out;
@@ -464,10 +457,8 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
out_path_release:
path_release(&nd);
out:
- current->act_as->fsuid = old_fsuid;
- current->act_as->fsgid = old_fsgid;
- current->act_as->cap_effective = old_cap;
-
+ current->act_as = old_act_as;
+ put_task_security(sec);
return res;
}

diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index a26c30e..25bda17 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -135,19 +135,6 @@ extern struct group_info init_groups;

extern struct task_security init_task_security;

-#define INIT_TASK_SECURITY(p) \
-{ \
- .usage = ATOMIC_INIT(3), \
- .keep_capabilities = 0, \
- .cap_inheritable = CAP_INIT_INH_SET, \
- .cap_permitted = CAP_FULL_SET, \
- .cap_effective = CAP_INIT_EFF_SET, \
- .cap_bset = CAP_INIT_BSET, \
- .user = INIT_USER, \
- .group_info = &init_groups, \
- .lock = __SPIN_LOCK_UNLOCKED(p.lock), \
-}
-
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -179,9 +166,8 @@ extern struct task_security init_task_security;
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
- .__temp_sec = INIT_TASK_SECURITY(tsk.__temp_sec), \
- .sec = &tsk.__temp_sec, \
- .act_as = &tsk.__temp_sec, \
+ .sec = &init_task_security, \
+ .act_as = &init_task_security, \
.comm = "swapper", \
.thread = INIT_THREAD, \
.fs = &init_fs, \
diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h
index e8b8a7a..f15ea9d 100644
--- a/include/linux/key-ui.h
+++ b/include/linux/key-ui.h
@@ -43,15 +43,13 @@ struct keyring_list {
* check to see whether permission is granted to use a key in the desired way
*/
extern int key_task_permission(const key_ref_t key_ref,
- struct task_struct *context,
+ struct task_security *sec,
key_perm_t perm);

-static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
-{
- return key_task_permission(key_ref, current, perm);
-}
+#define key_permission(key_ref, perm) \
+ key_task_permission((key_ref), current->act_as, (perm))

-extern key_ref_t lookup_user_key(struct task_struct *context,
+extern key_ref_t lookup_user_key(struct task_security *sec,
key_serial_t id, int create, int partial,
key_perm_t perm);

diff --git a/include/linux/key.h b/include/linux/key.h
index 163f864..ce07205 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -70,6 +70,8 @@ struct key;
struct seq_file;
struct user_struct;
struct signal_struct;
+struct task_security;
+struct thread_group_security;

struct key_type;
struct key_owner;
@@ -178,7 +180,7 @@ struct key {
extern struct key *key_alloc(struct key_type *type,
const char *desc,
uid_t uid, gid_t gid,
- struct task_struct *ctx,
+ struct task_security *sec,
key_perm_t perm,
unsigned long flags);

@@ -245,7 +247,7 @@ extern int key_unlink(struct key *keyring,
struct key *key);

extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- struct task_struct *ctx,
+ struct task_security *sec,
unsigned long flags,
struct key *dest);

@@ -267,24 +269,16 @@ extern struct key *key_lookup(key_serial_t id);
*/
extern struct key root_user_keyring, root_session_keyring;
extern int alloc_uid_keyring(struct user_struct *user,
- struct task_struct *ctx);
+ struct task_security *sec);
extern void switch_uid_keyring(struct user_struct *new_user);
-extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
-extern int copy_thread_group_keys(struct task_struct *tsk);
-extern void exit_keys(struct task_struct *tsk);
-extern void exit_thread_group_keys(struct signal_struct *tg);
+extern int copy_thread_group_keys(struct thread_group_security *tgsec);
extern int suid_keys(struct task_struct *tsk);
extern int exec_keys(struct task_struct *tsk);
-extern void key_fsuid_changed(struct task_struct *tsk);
-extern void key_fsgid_changed(struct task_struct *tsk);
+extern void key_fsuid_changed(struct task_security *sec);
+extern void key_fsgid_changed(struct task_security *sec);
extern void key_init(void);
-
-#define __install_session_keyring(tsk, keyring) \
-({ \
- struct key *old_session = tsk->signal->session_keyring; \
- tsk->signal->session_keyring = keyring; \
- old_session; \
-})
+extern void __install_session_keyring(struct task_struct *tsk,
+ struct key *keyring);

#else /* CONFIG_KEYS */

@@ -298,11 +292,8 @@ extern void key_init(void);
#define is_key_possessed(k) 0
#define alloc_uid_keyring(u,c) 0
#define switch_uid_keyring(u) do { } while(0)
-#define __install_session_keyring(t, k) ({ NULL; })
-#define copy_keys(f,t) 0
+#define __install_session_keyring(t, k) do {} while (0)
#define copy_thread_group_keys(t) 0
-#define exit_keys(t) do { } while(0)
-#define exit_thread_group_keys(tg) do { } while(0)
#define suid_keys(t) do { } while(0)
#define exec_keys(t) do { } while(0)
#define key_fsuid_changed(t) do { } while(0)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 33afa37..2bbdf85 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -525,12 +525,6 @@ struct signal_struct {

struct list_head cpu_timers[3];

- /* keep the process-shared keyrings here so that they do the right
- * thing in threads created with CLONE_THREAD */
-#ifdef CONFIG_KEYS
- struct key *session_keyring; /* keyring inherited over fork */
- struct key *process_keyring; /* keyring private to this process */
-#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
struct pacct_struct pacct; /* per-process accounting information */
#endif
@@ -608,6 +602,20 @@ extern struct user_struct root_user;

/*
+ * The common security details for a thread group
+ * - shared by CLONE_THREAD
+ */
+#ifdef CONFIG_KEYS
+struct thread_group_security {
+ atomic_t usage;
+ pid_t tgid; /* thread group process ID */
+ spinlock_t lock;
+ struct key *session_keyring; /* keyring inherited over fork */
+ struct key *process_keyring; /* keyring private to this process */
+};
+#endif
+
+/*
* The security context of a task
*
* The parts of the context break down into two categories:
@@ -650,6 +658,7 @@ struct task_security {
* keys to */
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
+ struct thread_group_security *tgsec;
#endif
#ifdef CONFIG_SECURITY
void *security; /* subjective LSM security */
@@ -659,10 +668,28 @@ struct task_security {
spinlock_t lock; /* lock for pointer changes */
};

+extern struct task_security *dup_task_security(struct task_security *);
+extern int copy_task_security(struct task_struct *, unsigned long);
+extern void put_task_security(struct task_security *);
+
#define current_fsuid() (current->act_as->fsuid)
#define current_fsgid() (current->act_as->fsgid)
#define current_cap() (current->act_as->cap_effective)

+/**
+ * get_task_security - Get an extra reference on a task security record
+ * @sec: The security record to get the reference on
+ *
+ * Get an extra reference on a task security record. The caller must arrange
+ * for this to be released.
+ */
+static inline
+struct task_security *get_task_security(struct task_security *sec)
+{
+ atomic_inc(&sec->usage);
+ return sec;
+}
+

struct backing_dev_info;
struct reclaim_state;
@@ -1155,7 +1182,6 @@ struct task_struct {
struct list_head cpu_timers[3];

/* process credentials */
- struct task_security __temp_sec __deprecated; /* temporary security to be removed */
struct task_security *sec; /* actual/objective task security */
struct task_security *act_as; /* effective/subjective task security */

diff --git a/include/linux/security.h b/include/linux/security.h
index a33fd03..9bf93c7 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -561,8 +561,13 @@ struct request_sock;
* allocated.
* Return 0 if operation was successful.
* @task_free_security:
- * @p contains the task_struct for process.
+ * @p points to the task_security struct to be freed.
* Deallocate and clear the p->security field.
+ * @task_dup_security:
+ * @p points to the task_security struct to be copied
+ * Duplicate and attach the security structure currently attached to the
+ * p->security field.
+ * Return 0 if operation was successful.
* @task_setuid:
* Check permission before setting one or more of the user identity
* attributes of the current process. The @flags parameter indicates
@@ -955,6 +960,7 @@ struct request_sock;
* Permit allocation of a key and assign security data. Note that key does
* not have a serial number assigned at this point.
* @key points to the key.
+ * @sec points to the task security record to use.
* @flags is the allocation flags
* Return 0 if permission is granted, -ve error otherwise.
* @key_free:
@@ -965,8 +971,8 @@ struct request_sock;
* See whether a specific operational right is granted to a process on a
* key.
* @key_ref refers to the key (key pointer + possession attribute bit).
- * @context points to the process to provide the context against which to
- * evaluate the security data on the key.
+ * @sec points to the process's security recored to provide the context
+ * against which to evaluate the security data on the key.
* @perm describes the combination of permissions required of this key.
* Return 1 if permission granted, 0 if permission denied and -ve it the
* normal permissions model should be effected.
@@ -1333,8 +1339,9 @@ struct security_operations {
int (*dentry_open) (struct file *file);

int (*task_create) (unsigned long clone_flags);
- int (*task_alloc_security) (struct task_struct * p);
- void (*task_free_security) (struct task_struct * p);
+ int (*task_alloc_security) (struct task_struct *p);
+ void (*task_free_security) (struct task_security *p);
+ int (*task_dup_security) (struct task_security *p);
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
uid_t old_euid, uid_t old_suid, int flags);
@@ -1465,10 +1472,11 @@ struct security_operations {

/* key management security hooks */
#ifdef CONFIG_KEYS
- int (*key_alloc)(struct key *key, struct task_struct *tsk, unsigned long flags);
+ int (*key_alloc)(struct key *key, struct task_security *context,
+ unsigned long flags);
void (*key_free)(struct key *key);
int (*key_permission)(key_ref_t key_ref,
- struct task_struct *context,
+ struct task_security *context,
key_perm_t perm);
int (*key_getsecurity)(struct key *key, char **_buffer);
#endif /* CONFIG_KEYS */
@@ -1589,7 +1597,8 @@ int security_file_receive(struct file *file);
int security_dentry_open(struct file *file);
int security_task_create(unsigned long clone_flags);
int security_task_alloc(struct task_struct *p);
-void security_task_free(struct task_struct *p);
+void security_task_free(struct task_security *p);
+int security_task_dup(struct task_security *p);
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags);
int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
uid_t old_suid, int flags);
@@ -2058,14 +2067,19 @@ static inline int security_task_create (unsigned long clone_flags)
return 0;
}

-static inline int security_task_alloc (struct task_struct *p)
+static inline int security_task_alloc(struct task_struct *p)
{
return 0;
}

-static inline void security_task_free (struct task_struct *p)
+static inline void security_task_free(struct task_security *p)
{ }

+static inline int security_task_dup(struct task_security *p)
+{
+ return 0;
+}
+
static inline int security_task_setuid (uid_t id0, uid_t id1, uid_t id2,
int flags)
{
@@ -2607,16 +2621,17 @@ static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi
#ifdef CONFIG_KEYS
#ifdef CONFIG_SECURITY

-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags);
+int security_key_alloc(struct key *key, struct task_security *sec,
+ unsigned long flags);
void security_key_free(struct key *key);
int security_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm);
+ struct task_security *sec, key_perm_t perm);
int security_key_getsecurity(struct key *key, char **_buffer);

#else

static inline int security_key_alloc(struct key *key,
- struct task_struct *tsk,
+ struct task_security *sec,
unsigned long flags)
{
return 0;
@@ -2627,7 +2642,7 @@ static inline void security_key_free(struct key *key)
}

static inline int security_key_permission(key_ref_t key_ref,
- struct task_struct *context,
+ struct task_security *sec,
key_perm_t perm)
{
return 0;
diff --git a/kernel/Makefile b/kernel/Makefile
index 685697c..afc1da1 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -8,7 +8,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
signal.o sys.o kmod.o workqueue.o pid.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 nsproxy.o srcu.o \
+ hrtimer.o rwsem.o nsproxy.o srcu.o cred.o \
utsname.o notifier.o ksysfs.o pm_qos_params.o

obj-$(CONFIG_SYSCTL) += sysctl_check.o
diff --git a/kernel/cred.c b/kernel/cred.c
new file mode 100644
index 0000000..298f26e
--- /dev/null
+++ b/kernel/cred.c
@@ -0,0 +1,140 @@
+/* Tasks security and 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.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/key.h>
+#include <linux/init_task.h>
+#include <linux/security.h>
+
+#ifdef CONFIG_KEYS
+static struct thread_group_security init_thread_group_security = {
+ .usage = ATOMIC_INIT(2),
+ .lock = __SPIN_LOCK_UNLOCKED(init_thread_group_security.lock),
+};
+#endif
+
+struct task_security init_task_security = {
+ .usage = ATOMIC_INIT(3),
+ .keep_capabilities = 0,
+ .cap_inheritable = CAP_INIT_INH_SET,
+ .cap_permitted = CAP_FULL_SET,
+ .cap_effective = CAP_INIT_EFF_SET,
+ .cap_bset = CAP_INIT_BSET,
+ .user = INIT_USER,
+#ifdef CONFIG_KEYS
+ .tgsec = &init_thread_group_security,
+#endif
+ .group_info = &init_groups,
+ .lock = __SPIN_LOCK_UNLOCKED(init_task_security.lock),
+};
+
+/**
+ * dup_task_security - Duplicate task security record
+ * @sec: The record to duplicate
+ *
+ * Returns a duplicate of a task security record or NULL if out of memory.
+ */
+struct task_security *dup_task_security(struct task_security *_sec)
+{
+ struct task_security *sec;
+
+ sec = kmemdup(_sec, sizeof(*sec), GFP_KERNEL);
+ if (sec) {
+ atomic_set(&sec->usage, 1);
+ get_uid(sec->user);
+ get_group_info(sec->group_info);
+ key_get(sec->thread_keyring);
+ key_get(sec->request_key_auth);
+ security_task_dup(sec);
+ }
+ return sec;
+}
+EXPORT_SYMBOL(dup_task_security);
+
+/**
+ * copy_task_security - Copy the task security records for fork
+ * @p: The new task
+ * @clone_flags: The details of what the new process shares of the old
+ *
+ * Copy the task security records on a task so that it can affect objects
+ * in the same way as its parent. Returns 0 if successful or -ENOMEM if out of
+ * memory.
+ */
+int copy_task_security(struct task_struct *p, unsigned long clone_flags)
+{
+ struct task_security *sec;
+
+ sec = kmemdup(p->sec, sizeof(*sec), GFP_KERNEL);
+ if (!sec)
+ return -ENOMEM;
+
+ atomic_set(&sec->usage, 2);
+ spin_lock_init(&sec->lock);
+ get_group_info(sec->group_info);
+ get_uid(p->sec->user);
+
+#ifdef CONFIG_KEYS
+ if (clone_flags & CLONE_THREAD) {
+ atomic_inc(&sec->tgsec->usage);
+ } else {
+ struct thread_group_security *tgsec;
+
+ tgsec = kmalloc(sizeof(*tgsec), GFP_KERNEL);
+ if (!tgsec) {
+ kfree(sec);
+ return -ENOMEM;
+ }
+ atomic_set(&tgsec->usage, 1);
+ spin_lock_init(&tgsec->lock);
+ tgsec->tgid = p->tgid;
+ copy_thread_group_keys(tgsec);
+ sec->tgsec = tgsec;
+ }
+ key_get(sec->request_key_auth);
+ sec->thread_keyring = NULL;
+#endif
+
+#ifdef CONFIG_SECURITY
+ sec->security = NULL;
+#endif
+
+ p->act_as = p->sec = sec;
+ return 0;
+}
+EXPORT_SYMBOL(copy_task_security);
+
+/**
+ * put_task_security - Release a ref on a task security record
+ * @sec: The record to release
+ *
+ * Release a reference to a task security record and destroy it when
+ * there are no references remaining.
+ */
+void put_task_security(struct task_security *sec)
+{
+ if (sec && atomic_dec_and_test(&sec->usage)) {
+ security_task_free(sec);
+ key_put(sec->thread_keyring);
+ key_put(sec->request_key_auth);
+ put_group_info(sec->group_info);
+ free_uid(sec->user);
+
+#ifdef CONFIG_KEYS
+ if (atomic_dec_and_test(&sec->tgsec->usage)) {
+ key_put(sec->tgsec->session_keyring);
+ key_put(sec->tgsec->process_keyring);
+ }
+#endif
+
+ kfree(sec);
+ }
+}
+EXPORT_SYMBOL(put_task_security);
diff --git a/kernel/exit.c b/kernel/exit.c
index 910c793..e94b5ab 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -998,7 +998,6 @@ fastcall NORET_TYPE void do_exit(long code)
check_stack_usage();
exit_thread();
cgroup_exit(tsk, 1);
- exit_keys(tsk);

if (group_dead && tsk->signal->leader)
disassociate_ctty(1);
diff --git a/kernel/fork.c b/kernel/fork.c
index 6580020..0c655b6 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -123,9 +123,8 @@ void __put_task_struct(struct task_struct *tsk)
WARN_ON(atomic_read(&tsk->usage));
WARN_ON(tsk == current);

- security_task_free(tsk);
- free_uid(tsk->__temp_sec.user);
- put_group_info(tsk->__temp_sec.group_info);
+ put_task_security(tsk->sec);
+ put_task_security(tsk->act_as);
delayacct_tsk_free(tsk);

if (!profile_handoff_task(tsk))
@@ -877,7 +876,6 @@ void __cleanup_sighand(struct sighand_struct *sighand)
static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
{
struct signal_struct *sig;
- int ret;

if (clone_flags & CLONE_THREAD) {
atomic_inc(&current->signal->count);
@@ -889,12 +887,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
if (!sig)
return -ENOMEM;

- ret = copy_thread_group_keys(tsk);
- if (ret < 0) {
- kmem_cache_free(signal_cachep, sig);
- return ret;
- }
-
atomic_set(&sig->count, 1);
atomic_set(&sig->live, 1);
init_waitqueue_head(&sig->wait_chldexit);
@@ -952,7 +944,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)

void __cleanup_signal(struct signal_struct *sig)
{
- exit_thread_group_keys(sig);
kmem_cache_free(signal_cachep, sig);
}

@@ -1046,18 +1037,19 @@ static struct task_struct *copy_process(unsigned long clone_flags,
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
- p->act_as = p->sec = &p->__temp_sec;
+ retval = copy_task_security(p, clone_flags);
+ if (retval < 0)
+ goto bad_fork_free;
+
retval = -EAGAIN;
if (atomic_read(&p->sec->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
p->sec->user != current->nsproxy->user_ns->root_user)
- goto bad_fork_free;
+ goto bad_fork_cleanup_put_task_sec;
}

- atomic_inc(&p->sec->user->__count);
atomic_inc(&p->sec->user->processes);
- get_group_info(p->sec->group_info);

/*
* If multiple threads are within copy_process(), then this check
@@ -1121,9 +1113,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
do_posix_clock_monotonic_gettime(&p->start_time);
p->real_start_time = p->start_time;
monotonic_to_bootbased(&p->real_start_time);
-#ifdef CONFIG_SECURITY
- p->sec->security = NULL;
-#endif
p->io_context = NULL;
p->audit_context = NULL;
cgroup_fork(p);
@@ -1171,7 +1160,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
- goto bad_fork_cleanup_security;
+ goto bad_fork_cleanup_policy;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
@@ -1185,10 +1174,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
- if ((retval = copy_keys(clone_flags, p)))
- goto bad_fork_cleanup_mm;
if ((retval = copy_namespaces(clone_flags, p)))
- goto bad_fork_cleanup_keys;
+ goto bad_fork_cleanup_mm;
if ((retval = copy_io(clone_flags, p)))
goto bad_fork_cleanup_namespaces;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
@@ -1364,8 +1351,6 @@ bad_fork_cleanup_io:
put_io_context(p->io_context);
bad_fork_cleanup_namespaces:
exit_task_namespaces(p);
-bad_fork_cleanup_keys:
- exit_keys(p);
bad_fork_cleanup_mm:
if (p->mm)
mmput(p->mm);
@@ -1381,8 +1366,6 @@ bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
-bad_fork_cleanup_security:
- security_task_free(p);
bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
mpol_free(p->mempolicy);
@@ -1395,9 +1378,10 @@ bad_fork_cleanup_cgroup:
bad_fork_cleanup_put_domain:
module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count:
- put_group_info(p->sec->group_info);
atomic_dec(&p->sec->user->processes);
- free_uid(p->sec->user);
+bad_fork_cleanup_put_task_sec:
+ put_task_security(p->act_as);
+ put_task_security(p->sec);
bad_fork_free:
free_task(p);
fork_out:
diff --git a/kernel/kmod.c b/kernel/kmod.c
index bb7df2a..7476b5f 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -133,20 +133,18 @@ struct subprocess_info {
static int ____call_usermodehelper(void *data)
{
struct subprocess_info *sub_info = data;
- struct key *new_session, *old_session;
int retval;

- /* Unblock all signals and set the session keyring. */
- new_session = key_get(sub_info->ring);
+ /* Set the session keyring. */
+ __install_session_keyring(current, sub_info->ring);
+
+ /* Unblock all signals. */
spin_lock_irq(&current->sighand->siglock);
- old_session = __install_session_keyring(current, new_session);
flush_signal_handlers(current, 1);
sigemptyset(&current->blocked);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);

- key_put(old_session);
-
/* Install input pipe when needed */
if (sub_info->stdin) {
struct files_struct *f = current->files;
diff --git a/kernel/sys.c b/kernel/sys.c
index b956bfe..f5d33f2 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -521,7 +521,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
sec->fsgid = new_egid;
sec->egid = new_egid;
sec->gid = new_rgid;
- key_fsgid_changed(current);
+ key_fsgid_changed(sec);
proc_id_connector(current, PROC_EVENT_GID);
return 0;
}
@@ -557,7 +557,7 @@ asmlinkage long sys_setgid(gid_t gid)
else
return -EPERM;

- key_fsgid_changed(current);
+ key_fsgid_changed(sec);
proc_id_connector(current, PROC_EVENT_GID);
return 0;
}
@@ -646,7 +646,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
sec->suid = sec->euid;
sec->fsuid = sec->euid;

- key_fsuid_changed(current);
+ key_fsuid_changed(sec);
proc_id_connector(current, PROC_EVENT_UID);

return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
@@ -694,7 +694,7 @@ asmlinkage long sys_setuid(uid_t uid)
sec->fsuid = sec->euid = uid;
sec->suid = new_suid;

- key_fsuid_changed(current);
+ key_fsuid_changed(sec);
proc_id_connector(current, PROC_EVENT_UID);

return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
@@ -744,7 +744,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
if (suid != (uid_t) -1)
sec->suid = suid;

- key_fsuid_changed(current);
+ key_fsuid_changed(sec);
proc_id_connector(current, PROC_EVENT_UID);

return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
@@ -798,7 +798,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
if (sgid != (gid_t) -1)
sec->sgid = sgid;

- key_fsgid_changed(current);
+ key_fsgid_changed(sec);
proc_id_connector(current, PROC_EVENT_GID);
return 0;
}
@@ -841,7 +841,7 @@ asmlinkage long sys_setfsuid(uid_t uid)
sec->fsuid = uid;
}

- key_fsuid_changed(current);
+ key_fsuid_changed(sec);
proc_id_connector(current, PROC_EVENT_UID);

security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
@@ -869,7 +869,7 @@ asmlinkage long sys_setfsgid(gid_t gid)
smp_wmb();
}
sec->fsgid = gid;
- key_fsgid_changed(current);
+ key_fsgid_changed(sec);
proc_id_connector(current, PROC_EVENT_GID);
}
return old_fsgid;
diff --git a/kernel/user.c b/kernel/user.c
index 3a2151d..0fa3a39 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -349,7 +349,7 @@ struct user_struct * alloc_uid(struct user_namespace *ns, uid_t uid)
#endif
new->locked_shm = 0;

- if (alloc_uid_keyring(new, current) < 0)
+ if (alloc_uid_keyring(new, current->sec) < 0)
goto out_free_user;

if (sched_create_user(new) < 0)
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index 9a8ff68..14979a5 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -297,7 +297,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,

_enter("");

- key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0,
+ key = key_alloc(&key_type_rxrpc, "x", 0, 0, current->act_as, 0,
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key)) {
_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
@@ -343,7 +343,7 @@ struct key *rxrpc_get_null_key(const char *keyname)
struct key *key;
int ret;

- key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
+ key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current->act_as,
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key))
return key;
diff --git a/security/dummy.c b/security/dummy.c
index 28413ae..cc516ab 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -503,16 +503,21 @@ static int dummy_task_create (unsigned long clone_flags)
return 0;
}

-static int dummy_task_alloc_security (struct task_struct *p)
+static int dummy_task_alloc_security(struct task_struct *p)
{
return 0;
}

-static void dummy_task_free_security (struct task_struct *p)
+static void dummy_task_free_security(struct task_security *sec)
{
return;
}

+static int dummy_task_dup_security(struct task_security *p)
+{
+ return 0;
+}
+
static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
{
return 0;
@@ -971,7 +976,7 @@ static void dummy_release_secctx(char *secdata, u32 seclen)
}

#ifdef CONFIG_KEYS
-static inline int dummy_key_alloc(struct key *key, struct task_struct *ctx,
+static inline int dummy_key_alloc(struct key *key, struct task_security *sec,
unsigned long flags)
{
return 0;
@@ -982,7 +987,7 @@ static inline void dummy_key_free(struct key *key)
}

static inline int dummy_key_permission(key_ref_t key_ref,
- struct task_struct *context,
+ struct task_security *sec,
key_perm_t perm)
{
return 0;
@@ -1087,6 +1092,7 @@ void security_fixup_ops (struct security_operations *ops)
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);
+ set_to_dummy_if_null(ops, task_dup_security);
set_to_dummy_if_null(ops, task_setuid);
set_to_dummy_if_null(ops, task_post_setuid);
set_to_dummy_if_null(ops, task_setgid);
diff --git a/security/keys/internal.h b/security/keys/internal.h
index f004835..a439889 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -92,7 +92,7 @@ extern struct key *keyring_search_instkey(struct key *keyring,
typedef int (*key_match_func_t)(const struct key *, const void *);

extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct task_struct *tsk,
+ struct task_security *sec,
struct key_type *type,
const void *description,
key_match_func_t match);
@@ -100,12 +100,12 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
extern key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *tsk);
+ struct task_security *sec);

extern struct key *find_keyring_by_name(const char *name, key_serial_t bound);

-extern int install_thread_keyring(struct task_struct *tsk);
-extern int install_process_keyring(struct task_struct *tsk);
+extern int install_thread_keyring(struct task_security *sec);
+extern int install_process_keyring(struct task_security *sec);

extern struct key *request_key_and_link(struct key_type *type,
const char *description,
@@ -120,7 +120,7 @@ extern struct key *request_key_and_link(struct key_type *type,
*/
struct request_key_auth {
struct key *target_key;
- struct task_struct *context;
+ struct task_security *sec;
void *callout_info;
size_t callout_len;
pid_t pid;
diff --git a/security/keys/key.c b/security/keys/key.c
index 4d1a253..f6ca221 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -243,7 +243,7 @@ serial_exists:
* instantiate the key or discard it before returning
*/
struct key *key_alloc(struct key_type *type, const char *desc,
- uid_t uid, gid_t gid, struct task_struct *ctx,
+ uid_t uid, gid_t gid, struct task_security *sec,
key_perm_t perm, unsigned long flags)
{
struct key_user *user = NULL;
@@ -314,7 +314,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
#endif

/* let the security module know about the key */
- ret = security_key_alloc(key, ctx, flags);
+ ret = security_key_alloc(key, sec, flags);
if (ret < 0)
goto security_error;

@@ -818,7 +818,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,

/* allocate a new key */
key = key_alloc(ktype, description, current_fsuid(), current_fsgid(),
- current, perm, flags);
+ current->act_as, perm, flags);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_3;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 4051948..2900451 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -878,7 +878,7 @@ long keyctl_instantiate_key(key_serial_t id,
* requesting task */
keyring_ref = NULL;
if (ringid) {
- keyring_ref = lookup_user_key(rka->context, ringid, 1, 0,
+ keyring_ref = lookup_user_key(rka->sec, ringid, 1, 0,
KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
@@ -973,13 +973,13 @@ long keyctl_set_reqkey_keyring(int reqkey_defl)

switch (reqkey_defl) {
case KEY_REQKEY_DEFL_THREAD_KEYRING:
- ret = install_thread_keyring(current);
+ ret = install_thread_keyring(sec);
if (ret < 0)
return ret;
goto set;

case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- ret = install_process_keyring(current);
+ ret = install_process_keyring(sec);
if (ret < 0)
return ret;

diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 76b89b2..6ccd8f8 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -244,14 +244,14 @@ static long keyring_read(const struct key *keyring,
* allocate a keyring and link into the destination keyring
*/
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- struct task_struct *ctx, unsigned long flags,
+ struct task_security *sec, unsigned long flags,
struct key *dest)
{
struct key *keyring;
int ret;

keyring = key_alloc(&key_type_keyring, description,
- uid, gid, ctx,
+ uid, gid, sec,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
flags);

@@ -280,7 +280,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
* - we propagate the possession attribute from the keyring ref to the key ref
*/
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct task_struct *context,
+ struct task_security *sec,
struct key_type *type,
const void *description,
key_match_func_t match)
@@ -303,7 +303,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
key_check(keyring);

/* top keyring must have search permission to begin the search */
- err = key_task_permission(keyring_ref, context, KEY_SEARCH);
+ err = key_task_permission(keyring_ref, sec, KEY_SEARCH);
if (err < 0) {
key_ref = ERR_PTR(err);
goto error;
@@ -376,7 +376,7 @@ descend:

/* key must have search permissions */
if (key_task_permission(make_key_ref(key, possessed),
- context, KEY_SEARCH) < 0)
+ sec, KEY_SEARCH) < 0)
continue;

/* we set a different error code if we pass a negative key */
@@ -403,7 +403,7 @@ ascend:
continue;

if (key_task_permission(make_key_ref(key, possessed),
- context, KEY_SEARCH) < 0)
+ sec, KEY_SEARCH) < 0)
continue;

/* stack the current position */
@@ -458,7 +458,7 @@ key_ref_t keyring_search(key_ref_t keyring,
if (!type->match)
return ERR_PTR(-ENOKEY);

- return keyring_search_aux(keyring, current,
+ return keyring_search_aux(keyring, current->sec,
type, description, type->match);

} /* end keyring_search() */
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 07898bd..eff3e29 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -19,10 +19,9 @@
* but permit the security modules to override
*/
int key_task_permission(const key_ref_t key_ref,
- struct task_struct *context,
+ struct task_security *sec,
key_perm_t perm)
{
- struct task_security *sec = context->act_as;
struct key *key;
key_perm_t kperm;
int ret;
@@ -69,7 +68,7 @@ use_these_perms:
return -EACCES;

/* let LSM be the final arbiter */
- return security_key_permission(key_ref, context, perm);
+ return security_key_permission(key_ref, sec, perm);

} /* end key_task_permission() */

diff --git a/security/keys/proc.c b/security/keys/proc.c
index 6941260..d83d5b5 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -141,7 +141,7 @@ static int proc_keys_show(struct seq_file *m, void *v)

/* check whether the current task is allowed to view the key (assuming
* non-possession) */
- rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW);
+ rc = key_task_permission(make_key_ref(key, 0), current->sec, KEY_VIEW);
if (rc < 0)
return 0;

diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index a016e9b..0bf03fa 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -68,7 +68,7 @@ struct key root_session_keyring = {
* allocate the keyrings to be associated with a UID
*/
int alloc_uid_keyring(struct user_struct *user,
- struct task_struct *ctx)
+ struct task_security *sec)
{
struct key *uid_keyring, *session_keyring;
char buf[20];
@@ -77,7 +77,7 @@ int alloc_uid_keyring(struct user_struct *user,
/* concoct a default session keyring */
sprintf(buf, "_uid_ses.%u", user->uid);

- session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
+ session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, sec,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
@@ -88,7 +88,7 @@ int alloc_uid_keyring(struct user_struct *user,
* keyring */
sprintf(buf, "_uid.%u", user->uid);

- uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
+ uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, sec,
KEY_ALLOC_IN_QUOTA, session_keyring);
if (IS_ERR(uid_keyring)) {
key_put(session_keyring);
@@ -135,33 +135,29 @@ void switch_uid_keyring(struct user_struct *new_user)

/*****************************************************************************/
/*
- * install a fresh thread keyring, discarding the old one
+ * make sure a thread keyring is installed
*/
-int install_thread_keyring(struct task_struct *tsk)
+int install_thread_keyring(struct task_security *sec)
{
- struct key *keyring, *old;
+ struct key *keyring;
char buf[20];
- int ret;

- sprintf(buf, "_tid.%u", tsk->pid);
+ sprintf(buf, "_tid.%u", current->pid);

- keyring = keyring_alloc(buf, tsk->sec->uid, tsk->sec->gid, tsk,
+ keyring = keyring_alloc(buf, sec->uid, sec->gid, sec,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
- }
-
- task_lock(tsk);
- old = tsk->sec->thread_keyring;
- tsk->sec->thread_keyring = keyring;
- task_unlock(tsk);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);

- ret = 0;
+ spin_lock(&sec->lock);
+ if (!sec->thread_keyring) {
+ sec->thread_keyring = keyring;
+ keyring = NULL;
+ }
+ spin_unlock(&sec->lock);

- key_put(old);
-error:
- return ret;
+ key_put(keyring);
+ return 0;

} /* end install_thread_keyring() */

@@ -169,38 +165,36 @@ error:
/*
* make sure a process keyring is installed
*/
-int install_process_keyring(struct task_struct *tsk)
+int install_process_keyring(struct task_security *sec)
{
+ struct thread_group_security *tgsec;
struct key *keyring;
char buf[20];
- int ret;

might_sleep();
+ sec = current->sec;
+ tgsec = sec->tgsec;

- if (!tsk->signal->process_keyring) {
- sprintf(buf, "_pid.%u", tsk->tgid);
+ if (!tgsec->process_keyring) {
+ sprintf(buf, "_pid.%u", tgsec->tgid);

- keyring = keyring_alloc(buf, tsk->sec->uid, tsk->sec->gid, tsk,
+ keyring = keyring_alloc(buf, sec->uid, sec->gid, sec,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
- }
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);

/* attach keyring */
- spin_lock_irq(&tsk->sighand->siglock);
- if (!tsk->signal->process_keyring) {
- tsk->signal->process_keyring = keyring;
+ spin_lock(&tgsec->lock);
+ if (!tgsec->process_keyring) {
+ tgsec->process_keyring = keyring;
keyring = NULL;
}
- spin_unlock_irq(&tsk->sighand->siglock);
+ spin_unlock(&tgsec->lock);

key_put(keyring);
}

- ret = 0;
-error:
- return ret;
+ return 0;

} /* end install_process_keyring() */

@@ -209,37 +203,38 @@ error:
* install a session keyring, discarding the old one
* - if a keyring is not supplied, an empty one is invented
*/
-static int install_session_keyring(struct task_struct *tsk,
+static int install_session_keyring(struct task_security *sec,
struct key *keyring)
{
+ struct thread_group_security *tgsec;
unsigned long flags;
struct key *old;
char buf[20];

might_sleep();
+ tgsec = sec->tgsec;

/* create an empty session keyring */
if (!keyring) {
- sprintf(buf, "_ses.%u", tsk->tgid);
+ sprintf(buf, "_ses.%u", tgsec->tgid);

flags = KEY_ALLOC_QUOTA_OVERRUN;
- if (tsk->signal->session_keyring)
+ if (tgsec->session_keyring)
flags = KEY_ALLOC_IN_QUOTA;

- keyring = keyring_alloc(buf, tsk->sec->uid, tsk->sec->gid, tsk,
+ keyring = keyring_alloc(buf, sec->uid, sec->gid, sec,
flags, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
- }
- else {
+ } else {
atomic_inc(&keyring->usage);
}

/* install the keyring */
- spin_lock_irq(&tsk->sighand->siglock);
- old = tsk->signal->session_keyring;
- rcu_assign_pointer(tsk->signal->session_keyring, keyring);
- spin_unlock_irq(&tsk->sighand->siglock);
+ spin_lock_irq(&tgsec->lock);
+ old = tgsec->session_keyring;
+ rcu_assign_pointer(tgsec->session_keyring, keyring);
+ spin_unlock_irq(&tgsec->lock);

/* we're using RCU on the pointer, but there's no point synchronising
* on it if it didn't previously point to anything */
@@ -252,68 +247,49 @@ static int install_session_keyring(struct task_struct *tsk,

} /* end install_session_keyring() */

-/*****************************************************************************/
/*
- * copy the keys in a thread group for fork without CLONE_THREAD
+ * install a session keyring for kmod
*/
-int copy_thread_group_keys(struct task_struct *tsk)
+void __install_session_keyring(struct task_struct *tsk, struct key *keyring)
{
- key_check(current->thread_group->session_keyring);
- key_check(current->thread_group->process_keyring);
-
- /* no process keyring yet */
- tsk->signal->process_keyring = NULL;
+ struct thread_group_security *tgsec = tsk->sec->tgsec;
+ struct key *old;

- /* same session keyring */
- rcu_read_lock();
- tsk->signal->session_keyring =
- key_get(rcu_dereference(current->signal->session_keyring));
- rcu_read_unlock();
+ key_get(keyring);

- return 0;
+ spin_lock(&tgsec->lock);
+ old = tgsec->session_keyring;
+ rcu_assign_pointer(tgsec->session_keyring, keyring);
+ spin_unlock(&tgsec->lock);

-} /* end copy_thread_group_keys() */
+ /* we're using RCU on the pointer, but there's no point synchronising
+ * on it if it didn't previously point to anything */
+ if (old) {
+ synchronize_rcu();
+ key_put(old);
+ }
+}

/*****************************************************************************/
/*
- * copy the keys for fork
+ * copy the keys in a thread group for fork without CLONE_THREAD
*/
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+int copy_thread_group_keys(struct thread_group_security *tgsec)
{
- key_check(tsk->sec->thread_keyring);
- key_check(tsk->sec->request_key_auth);
+ key_check(tgsec->session_keyring);
+ key_check(tgsec->process_keyring);

- /* no thread keyring yet */
- tsk->sec->thread_keyring = NULL;
+ /* no process keyring yet */
+ tgsec->process_keyring = NULL;

- /* copy the request_key() authorisation for this thread */
- key_get(tsk->sec->request_key_auth);
+ /* same session keyring */
+ rcu_read_lock();
+ tgsec->session_keyring =
+ key_get(rcu_dereference(current->sec->tgsec->session_keyring));
+ rcu_read_unlock();

return 0;
-
-} /* end copy_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of thread group keys upon thread group destruction
- */
-void exit_thread_group_keys(struct signal_struct *tg)
-{
- key_put(tg->session_keyring);
- key_put(tg->process_keyring);
-
-} /* end exit_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of per-thread keys upon thread exit
- */
-void exit_keys(struct task_struct *tsk)
-{
- key_put(tsk->sec->thread_keyring);
- key_put(tsk->sec->request_key_auth);
-
-} /* end exit_keys() */
+}

/*****************************************************************************/
/*
@@ -321,21 +297,23 @@ void exit_keys(struct task_struct *tsk)
*/
int exec_keys(struct task_struct *tsk)
{
+ struct thread_group_security *tgsec = tsk->sec->tgsec;
+ struct task_security *sec = tsk->sec;
struct key *old;

/* newly exec'd tasks don't get a thread keyring */
- task_lock(tsk);
- old = tsk->sec->thread_keyring;
- tsk->sec->thread_keyring = NULL;
- task_unlock(tsk);
+ spin_lock(&sec->lock);
+ old = sec->thread_keyring;
+ sec->thread_keyring = NULL;
+ spin_unlock(&sec->lock);

key_put(old);

/* discard the process keyring from a newly exec'd task */
- spin_lock_irq(&tsk->sighand->siglock);
- old = tsk->signal->process_keyring;
- tsk->signal->process_keyring = NULL;
- spin_unlock_irq(&tsk->sighand->siglock);
+ spin_lock(&tgsec->lock);
+ old = tgsec->process_keyring;
+ tgsec->process_keyring = NULL;
+ spin_unlock(&tgsec->lock);

key_put(old);

@@ -358,14 +336,13 @@ int suid_keys(struct task_struct *tsk)
/*
* the filesystem user ID changed
*/
-void key_fsuid_changed(struct task_struct *tsk)
+void key_fsuid_changed(struct task_security *sec)
{
/* update the ownership of the thread keyring */
- BUG_ON(!tsk->sec);
- if (tsk->sec->thread_keyring) {
- down_write(&tsk->sec->thread_keyring->sem);
- tsk->sec->thread_keyring->uid = tsk->sec->fsuid;
- up_write(&tsk->sec->thread_keyring->sem);
+ if (sec->thread_keyring) {
+ down_write(&sec->thread_keyring->sem);
+ sec->thread_keyring->uid = sec->fsuid;
+ up_write(&sec->thread_keyring->sem);
}

} /* end key_fsuid_changed() */
@@ -374,14 +351,13 @@ void key_fsuid_changed(struct task_struct *tsk)
/*
* the filesystem group ID changed
*/
-void key_fsgid_changed(struct task_struct *tsk)
+void key_fsgid_changed(struct task_security *sec)
{
/* update the ownership of the thread keyring */
- BUG_ON(!tsk->sec);
- if (tsk->sec->thread_keyring) {
- down_write(&tsk->sec->thread_keyring->sem);
- tsk->sec->thread_keyring->gid = tsk->sec->fsgid;
- up_write(&tsk->sec->thread_keyring->sem);
+ if (sec->thread_keyring) {
+ down_write(&sec->thread_keyring->sem);
+ sec->thread_keyring->gid = sec->fsgid;
+ up_write(&sec->thread_keyring->sem);
}

} /* end key_fsgid_changed() */
@@ -397,7 +373,7 @@ void key_fsgid_changed(struct task_struct *tsk)
key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *context)
+ struct task_security *sec)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret, err;
@@ -416,10 +392,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
err = ERR_PTR(-EAGAIN);

/* search the thread keyring first */
- if (context->sec->thread_keyring) {
+ if (sec->thread_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->sec->thread_keyring, 1),
- context, type, description, match);
+ make_key_ref(sec->thread_keyring, 1),
+ sec, type, description, match);
if (!IS_ERR(key_ref))
goto found;

@@ -437,10 +413,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
}

/* search the process keyring second */
- if (context->signal->process_keyring) {
+ if (sec->tgsec->process_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->signal->process_keyring, 1),
- context, type, description, match);
+ make_key_ref(sec->tgsec->process_keyring, 1),
+ sec, type, description, match);
if (!IS_ERR(key_ref))
goto found;

@@ -458,13 +434,13 @@ key_ref_t search_process_keyrings(struct key_type *type,
}

/* search the session keyring */
- if (context->signal->session_keyring) {
+ if (sec->tgsec->session_keyring) {
rcu_read_lock();
key_ref = keyring_search_aux(
make_key_ref(rcu_dereference(
- context->signal->session_keyring),
+ sec->tgsec->session_keyring),
1),
- context, type, description, match);
+ sec, type, description, match);
rcu_read_unlock();

if (!IS_ERR(key_ref))
@@ -485,8 +461,8 @@ key_ref_t search_process_keyrings(struct key_type *type,
/* or search the user-session keyring */
else {
key_ref = keyring_search_aux(
- make_key_ref(context->sec->user->session_keyring, 1),
- context, type, description, match);
+ make_key_ref(sec->user->session_keyring, 1),
+ sec, type, description, match);
if (!IS_ERR(key_ref))
goto found;

@@ -507,20 +483,20 @@ key_ref_t search_process_keyrings(struct key_type *type,
* search the keyrings of the process mentioned there
* - we don't permit access to request_key auth keys via this method
*/
- if (context->sec->request_key_auth &&
- context == current &&
+ if (sec->request_key_auth &&
+ sec == current->sec &&
type != &key_type_request_key_auth
) {
/* defend against the auth key being revoked */
- down_read(&context->sec->request_key_auth->sem);
+ down_read(&sec->request_key_auth->sem);

- if (key_validate(context->sec->request_key_auth) == 0) {
- rka = context->sec->request_key_auth->payload.data;
+ if (key_validate(sec->request_key_auth) == 0) {
+ rka = sec->request_key_auth->payload.data;

key_ref = search_process_keyrings(type, description,
- match, rka->context);
+ match, rka->sec);

- up_read(&context->sec->request_key_auth->sem);
+ up_read(&sec->request_key_auth->sem);

if (!IS_ERR(key_ref))
goto found;
@@ -537,7 +513,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
break;
}
} else {
- up_read(&context->sec->request_key_auth->sem);
+ up_read(&sec->request_key_auth->sem);
}
}

@@ -565,78 +541,78 @@ static int lookup_user_key_possessed(const struct key *key, const void *target)
* - don't create special keyrings unless so requested
* - partially constructed keys aren't found unless requested
*/
-key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
+key_ref_t lookup_user_key(struct task_security *sec, key_serial_t id,
int create, int partial, key_perm_t perm)
{
key_ref_t key_ref, skey_ref;
struct key *key;
int ret;

- if (!context)
- context = current;
+ if (!sec)
+ sec = current->act_as;

key_ref = ERR_PTR(-ENOKEY);

switch (id) {
case KEY_SPEC_THREAD_KEYRING:
- if (!context->sec->thread_keyring) {
+ if (!sec->thread_keyring) {
if (!create)
goto error;

- ret = install_thread_keyring(context);
+ ret = install_thread_keyring(sec);
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
}

- key = context->sec->thread_keyring;
+ key = sec->thread_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;

case KEY_SPEC_PROCESS_KEYRING:
- if (!context->signal->process_keyring) {
+ if (!sec->tgsec->process_keyring) {
if (!create)
goto error;

- ret = install_process_keyring(context);
+ ret = install_process_keyring(sec);
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
}

- key = context->signal->process_keyring;
+ key = sec->tgsec->process_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;

case KEY_SPEC_SESSION_KEYRING:
- if (!context->signal->session_keyring) {
+ if (!sec->tgsec->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
ret = install_session_keyring(
- context, context->sec->user->session_keyring);
+ sec, sec->user->session_keyring);
if (ret < 0)
goto error;
}

rcu_read_lock();
- key = rcu_dereference(context->signal->session_keyring);
+ key = rcu_dereference(sec->tgsec->session_keyring);
atomic_inc(&key->usage);
rcu_read_unlock();
key_ref = make_key_ref(key, 1);
break;

case KEY_SPEC_USER_KEYRING:
- key = context->sec->user->uid_keyring;
+ key = sec->user->uid_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;

case KEY_SPEC_USER_SESSION_KEYRING:
- key = context->sec->user->session_keyring;
+ key = sec->user->session_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
@@ -647,7 +623,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
goto error;

case KEY_SPEC_REQKEY_AUTH_KEY:
- key = context->sec->request_key_auth;
+ key = sec->request_key_auth;
if (!key)
goto error;

@@ -671,7 +647,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
/* check to see if we possess the key */
skey_ref = search_process_keyrings(key->type, key,
lookup_user_key_possessed,
- current);
+ sec);

if (!IS_ERR(skey_ref)) {
key_put(key);
@@ -703,7 +679,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
goto invalid_key;

/* check the permissions */
- ret = key_task_permission(key_ref, context, perm);
+ ret = key_task_permission(key_ref, sec, perm);
if (ret < 0)
goto invalid_key;

@@ -726,18 +702,18 @@ invalid_key:
*/
long join_session_keyring(const char *name)
{
- struct task_struct *tsk = current;
+ struct task_security *sec = current->sec;
struct key *keyring;
long ret;

/* if no name is provided, install an anonymous keyring */
if (!name) {
- ret = install_session_keyring(tsk, NULL);
+ ret = install_session_keyring(sec, NULL);
if (ret < 0)
goto error;

rcu_read_lock();
- ret = rcu_dereference(tsk->signal->session_keyring)->serial;
+ ret = rcu_dereference(sec->tgsec->session_keyring)->serial;
rcu_read_unlock();
goto error;
}
@@ -749,7 +725,7 @@ long join_session_keyring(const char *name)
keyring = find_keyring_by_name(name, 0);
if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */
- keyring = keyring_alloc(name, tsk->sec->uid, tsk->sec->gid, tsk,
+ keyring = keyring_alloc(name, sec->uid, sec->gid, sec,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
@@ -762,7 +738,7 @@ long join_session_keyring(const char *name)
}

/* we've got a keyring - now to install it */
- ret = install_session_keyring(tsk, keyring);
+ ret = install_session_keyring(sec, keyring);
if (ret < 0)
goto error2;

diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 5b5ad42..00ee92e 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -63,7 +63,7 @@ static int call_sbin_request_key(struct key_construction *cons,
const char *op,
void *aux)
{
- struct task_struct *tsk = current;
+ struct task_security *sec = current->act_as;
key_serial_t prkey, sskey;
struct key *key = cons->key, *authkey = cons->authkey, *keyring;
char *argv[9], *envp[3], uid_str[12], gid_str[12];
@@ -76,7 +76,7 @@ static int call_sbin_request_key(struct key_construction *cons,
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);

- keyring = keyring_alloc(desc, current_fsuid(), current_fsgid(), current,
+ keyring = keyring_alloc(desc, sec->fsuid, sec->fsgid, sec,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
@@ -89,29 +89,27 @@ static int call_sbin_request_key(struct key_construction *cons,
goto error_link;

/* record the UID and GID */
- sprintf(uid_str, "%d", current_fsuid());
- sprintf(gid_str, "%d", current_fsgid());
+ sprintf(uid_str, "%d", sec->fsuid);
+ sprintf(gid_str, "%d", sec->fsgid);

/* we say which key is under construction */
sprintf(key_str, "%d", key->serial);

/* we specify the process's default keyrings */
- sprintf(keyring_str[0], "%d",
- tsk->act_as->thread_keyring ?
- tsk->act_as->thread_keyring->serial : 0);
+ sprintf(keyring_str[0], "%d", key_serial(sec->thread_keyring));

prkey = 0;
- if (tsk->signal->process_keyring)
- prkey = tsk->signal->process_keyring->serial;
+ if (sec->tgsec->process_keyring)
+ prkey = sec->tgsec->process_keyring->serial;

sprintf(keyring_str[1], "%d", prkey);

- if (tsk->signal->session_keyring) {
+ if (sec->tgsec->session_keyring) {
rcu_read_lock();
- sskey = rcu_dereference(tsk->signal->session_keyring)->serial;
+ sskey = rcu_dereference(sec->tgsec->session_keyring)->serial;
rcu_read_unlock();
} else {
- sskey = tsk->act_as->user->session_keyring->serial;
+ sskey = sec->user->session_keyring->serial;
}

sprintf(keyring_str[2], "%d", sskey);
@@ -210,29 +208,29 @@ static int construct_key(struct key *key, const void *callout_info,
*/
static void construct_key_make_link(struct key *key, struct key *dest_keyring)
{
- struct task_struct *tsk = current;
+ struct task_security *sec = current->sec;
struct key *drop = NULL;

kenter("{%d},%p", key->serial, dest_keyring);

/* find the appropriate keyring */
if (!dest_keyring) {
- switch (tsk->act_as->jit_keyring) {
+ switch (sec->jit_keyring) {
case KEY_REQKEY_DEFL_DEFAULT:
case KEY_REQKEY_DEFL_THREAD_KEYRING:
- dest_keyring = tsk->act_as->thread_keyring;
+ dest_keyring = sec->thread_keyring;
if (dest_keyring)
break;

case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- dest_keyring = tsk->signal->process_keyring;
+ dest_keyring = sec->tgsec->process_keyring;
if (dest_keyring)
break;

case KEY_REQKEY_DEFL_SESSION_KEYRING:
rcu_read_lock();
dest_keyring = key_get(
- rcu_dereference(tsk->signal->session_keyring));
+ rcu_dereference(sec->tgsec->session_keyring));
rcu_read_unlock();
drop = dest_keyring;

@@ -240,11 +238,11 @@ static void construct_key_make_link(struct key *key, struct key *dest_keyring)
break;

case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- dest_keyring = tsk->act_as->user->session_keyring;
+ dest_keyring = sec->user->session_keyring;
break;

case KEY_REQKEY_DEFL_USER_KEYRING:
- dest_keyring = tsk->act_as->user->uid_keyring;
+ dest_keyring = sec->user->uid_keyring;
break;

case KEY_REQKEY_DEFL_GROUP_KEYRING:
@@ -268,6 +266,7 @@ static int construct_alloc_key(struct key_type *type,
const char *description,
struct key *dest_keyring,
unsigned long flags,
+ struct task_security *sec,
struct key_user *user,
struct key **_key)
{
@@ -278,9 +277,8 @@ static int construct_alloc_key(struct key_type *type,

mutex_lock(&user->cons_lock);

- key = key_alloc(type, description,
- current_fsuid(), current_fsgid(), current, KEY_POS_ALL,
- flags);
+ key = key_alloc(type, description, sec->fsuid, sec->fsgid, sec,
+ KEY_POS_ALL, flags);
if (IS_ERR(key))
goto alloc_failed;

@@ -294,8 +292,7 @@ static int construct_alloc_key(struct key_type *type,
* waited for locks */
mutex_lock(&key_construction_mutex);

- key_ref = search_process_keyrings(type, description, type->match,
- current);
+ key_ref = search_process_keyrings(type, description, type->match, sec);
if (!IS_ERR(key_ref))
goto key_already_present;

@@ -336,18 +333,19 @@ static struct key *construct_key_and_link(struct key_type *type,
size_t callout_len,
void *aux,
struct key *dest_keyring,
+ struct task_security *sec,
unsigned long flags)
{
struct key_user *user;
struct key *key;
int ret;

- user = key_user_lookup(current_fsuid());
+ user = key_user_lookup(sec->fsuid);
if (!user)
return ERR_PTR(-ENOMEM);

- ret = construct_alloc_key(type, description, dest_keyring, flags, user,
- &key);
+ ret = construct_alloc_key(type, description, dest_keyring, flags, sec,
+ user, &key);
key_user_put(user);

if (ret == 0) {
@@ -379,6 +377,7 @@ struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags)
{
+ struct task_security *sec = current->sec;
struct key *key;
key_ref_t key_ref;

@@ -387,9 +386,7 @@ struct key *request_key_and_link(struct key_type *type,
dest_keyring, flags);

/* search all the process keyrings for a key */
- key_ref = search_process_keyrings(type, description, type->match,
- current);
-
+ key_ref = search_process_keyrings(type, description, type->match, sec);
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
} else if (PTR_ERR(key_ref) != -EAGAIN) {
@@ -403,7 +400,7 @@ struct key *request_key_and_link(struct key_type *type,

key = construct_key_and_link(type, description, callout_info,
callout_len, aux, dest_keyring,
- flags);
+ sec, flags);
}

error:
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index d306412..2513d90 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -104,10 +104,8 @@ static void request_key_auth_revoke(struct key *key)

kenter("{%d}", key->serial);

- if (rka->context) {
- put_task_struct(rka->context);
- rka->context = NULL;
- }
+ put_task_security(rka->sec);
+ rka->sec = NULL;

} /* end request_key_auth_revoke() */

@@ -121,11 +119,7 @@ static void request_key_auth_destroy(struct key *key)

kenter("{%d}", key->serial);

- if (rka->context) {
- put_task_struct(rka->context);
- rka->context = NULL;
- }
-
+ put_task_security(rka->sec);
key_put(rka->target_key);
kfree(rka->callout_info);
kfree(rka);
@@ -141,6 +135,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
size_t callout_len)
{
struct request_key_auth *rka, *irka;
+ struct task_security *sec = current->sec;
struct key *authkey = NULL;
char desc[20];
int ret;
@@ -162,28 +157,25 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,

/* see if the calling process is already servicing the key request of
* another process */
- if (current->act_as->request_key_auth) {
+ if (sec->request_key_auth) {
/* it is - use that instantiation context here too */
- down_read(&current->act_as->request_key_auth->sem);
+ down_read(&sec->request_key_auth->sem);

/* if the auth key has been revoked, then the key we're
* servicing is already instantiated */
- if (test_bit(KEY_FLAG_REVOKED,
- &current->act_as->request_key_auth->flags))
+ if (test_bit(KEY_FLAG_REVOKED, &sec->request_key_auth->flags))
goto auth_key_revoked;

- irka = current->act_as->request_key_auth->payload.data;
- rka->context = irka->context;
+ irka = sec->request_key_auth->payload.data;
+ rka->sec = irka->sec;
rka->pid = irka->pid;
- get_task_struct(rka->context);
+ get_task_security(rka->sec);

- up_read(&current->act_as->request_key_auth->sem);
- }
- else {
+ up_read(&sec->request_key_auth->sem);
+ } else {
/* it isn't - use this process as the context */
- rka->context = current;
+ rka->sec = get_task_security(sec);
rka->pid = current->pid;
- get_task_struct(rka->context);
}

rka->target_key = key_get(target);
@@ -194,7 +186,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
sprintf(desc, "%x", target->serial);

authkey = key_alloc(&key_type_request_key_auth, desc,
- current_fsuid(), current_fsgid(), current,
+ sec->fsuid, sec->fsgid, sec,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(authkey)) {
@@ -260,7 +252,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
&key_type_request_key_auth,
(void *) (unsigned long) target_id,
key_get_instantiation_authkey_match,
- current);
+ current->act_as);

if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
diff --git a/security/security.c b/security/security.c
index 3e75b90..3aceeac 100644
--- a/security/security.c
+++ b/security/security.c
@@ -590,9 +590,14 @@ int security_task_alloc(struct task_struct *p)
return security_ops->task_alloc_security(p);
}

-void security_task_free(struct task_struct *p)
+void security_task_free(struct task_security *sec)
{
- security_ops->task_free_security(p);
+ security_ops->task_free_security(sec);
+}
+
+int security_task_dup(struct task_security *sec)
+{
+ return security_ops->task_dup_security(sec);
}

int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -1086,9 +1091,10 @@ EXPORT_SYMBOL(security_skb_classify_flow);

#ifdef CONFIG_KEYS

-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags)
+int security_key_alloc(struct key *key, struct task_security *sec,
+ unsigned long flags)
{
- return security_ops->key_alloc(key, tsk, flags);
+ return security_ops->key_alloc(key, sec, flags);
}

void security_key_free(struct key *key)
@@ -1096,10 +1102,10 @@ void security_key_free(struct key *key)
security_ops->key_free(key);
}

-int security_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm)
+int security_key_permission(key_ref_t key_ref, struct task_security *sec,
+ key_perm_t perm)
{
- return security_ops->key_permission(key_ref, context, perm);
+ return security_ops->key_permission(key_ref, sec, perm);
}

int security_key_getsecurity(struct key *key, char **_buffer)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 74cd7e2..ea9e0a8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -168,10 +168,10 @@ static int task_alloc_security(struct task_struct *task)
return 0;
}

-static void task_free_security(struct task_struct *task)
+static void task_free_security(struct task_security *sec)
{
- struct task_security_struct *tsec = task->sec->security;
- task->sec->security = NULL;
+ struct task_security_struct *tsec = sec->security;
+ sec->security = NULL;
kfree(tsec);
}

@@ -3064,9 +3064,32 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
return 0;
}

-static void selinux_task_free_security(struct task_struct *tsk)
+static void selinux_task_free_security(struct task_security *sec)
{
- task_free_security(tsk);
+ task_free_security(sec);
+}
+
+static int selinux_task_dup_security(struct task_security *sec)
+{
+ struct task_security_struct *tsec1, *tsec2;
+
+ tsec1 = sec->security;
+
+ tsec2 = kmemdup(tsec1, sizeof(*tsec1), GFP_KERNEL);
+ if (!tsec2)
+ return -ENOMEM;
+
+ tsec2->osid = tsec1->osid;
+ tsec2->sid = tsec1->sid;
+
+ tsec2->exec_sid = tsec1->exec_sid;
+ tsec2->create_sid = tsec1->create_sid;
+ tsec2->keycreate_sid = tsec1->keycreate_sid;
+ tsec2->sockcreate_sid = tsec1->sockcreate_sid;
+ tsec2->ptrace_sid = SECINITSID_UNLABELED;
+ sec->security = tsec2;
+
+ return 0;
}

static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -5153,10 +5176,10 @@ static void selinux_release_secctx(char *secdata, u32 seclen)

#ifdef CONFIG_KEYS

-static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+static int selinux_key_alloc(struct key *k, struct task_security *context,
unsigned long flags)
{
- struct task_security_struct *tsec = tsk->sec->security;
+ struct task_security_struct *tsec = context->security;
struct key_security_struct *ksec;

ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
@@ -5182,7 +5205,7 @@ static void selinux_key_free(struct key *k)
}

static int selinux_key_permission(key_ref_t key_ref,
- struct task_struct *ctx,
+ struct task_security *context,
key_perm_t perm)
{
struct key *key;
@@ -5191,7 +5214,7 @@ static int selinux_key_permission(key_ref_t key_ref,

key = key_ref_to_ptr(key_ref);

- tsec = ctx->sec->security;
+ tsec = context->security;
ksec = key->security;

/* if no specific permissions are requested, we skip the
@@ -5298,6 +5321,7 @@ static struct security_operations selinux_ops = {
.task_create = selinux_task_create,
.task_alloc_security = selinux_task_alloc_security,
.task_free_security = selinux_task_free_security,
+ .task_dup_security = selinux_task_dup_security,
.task_setuid = selinux_task_setuid,
.task_post_setuid = selinux_task_post_setuid,
.task_setgid = selinux_task_setgid,
@@ -5440,9 +5464,9 @@ static __init int selinux_init(void)

#ifdef CONFIG_KEYS
/* Add security information to initial keyrings */
- selinux_key_alloc(&root_user_keyring, current,
+ selinux_key_alloc(&root_user_keyring, current->sec,
KEY_ALLOC_NOT_IN_QUOTA);
- selinux_key_alloc(&root_session_keyring, current,
+ selinux_key_alloc(&root_session_keyring, current->sec,
KEY_ALLOC_NOT_IN_QUOTA);
#endif