summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-11-14 10:39:17 +1100
committerJames Morris <jmorris@namei.org>2008-11-14 10:39:17 +1100
commitf1752eec6145c97163dbce62d17cf5d928e28a27 (patch)
tree16bc51166d38815092de36a461b845b0b4b522f9 /kernel
parentb6dff3ec5e116e3af6f537d4caedcad6b9e5082a (diff)
CRED: Detach the credentials from task_struct
Detach the credentials from task_struct, duplicating them in copy_process() and releasing them in __put_task_struct(). Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: James Morris <jmorris@namei.org> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/cred.c96
-rw-r--r--kernel/fork.c24
3 files changed, 103 insertions, 19 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 9a3ec66a9d8..5a6a612c302 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.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 semaphore.o \
- notifier.o ksysfs.o pm_qos_params.o sched_clock.o
+ notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o
CFLAGS_REMOVE_sched.o = -mno-spe
diff --git a/kernel/cred.c b/kernel/cred.c
new file mode 100644
index 00000000000..833244a7cb0
--- /dev/null
+++ b/kernel/cred.c
@@ -0,0 +1,96 @@
+/* Task credentials management
+ *
+ * Copyright (C) 2008 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/cred.h>
+#include <linux/sched.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/init_task.h>
+#include <linux/security.h>
+
+/*
+ * The initial credentials for the initial task
+ */
+struct cred init_cred = {
+ .usage = ATOMIC_INIT(3),
+ .securebits = SECUREBITS_DEFAULT,
+ .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,
+};
+
+/*
+ * The RCU callback to actually dispose of a set of credentials
+ */
+static void put_cred_rcu(struct rcu_head *rcu)
+{
+ struct cred *cred = container_of(rcu, struct cred, rcu);
+
+ BUG_ON(atomic_read(&cred->usage) != 0);
+
+ key_put(cred->thread_keyring);
+ key_put(cred->request_key_auth);
+ put_group_info(cred->group_info);
+ free_uid(cred->user);
+ security_cred_free(cred);
+ kfree(cred);
+}
+
+/**
+ * __put_cred - Destroy a set of credentials
+ * @sec: The record to release
+ *
+ * Destroy a set of credentials on which no references remain.
+ */
+void __put_cred(struct cred *cred)
+{
+ call_rcu(&cred->rcu, put_cred_rcu);
+}
+EXPORT_SYMBOL(__put_cred);
+
+/*
+ * Copy credentials for the new process created by fork()
+ */
+int copy_creds(struct task_struct *p, unsigned long clone_flags)
+{
+ struct cred *pcred;
+ int ret;
+
+ pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL);
+ if (!pcred)
+ return -ENOMEM;
+
+#ifdef CONFIG_SECURITY
+ pcred->security = NULL;
+#endif
+
+ ret = security_cred_alloc(pcred);
+ if (ret < 0) {
+ kfree(pcred);
+ return ret;
+ }
+
+ atomic_set(&pcred->usage, 1);
+ get_group_info(pcred->group_info);
+ get_uid(pcred->user);
+ key_get(pcred->thread_keyring);
+ key_get(pcred->request_key_auth);
+
+ atomic_inc(&pcred->user->processes);
+
+ /* RCU assignment is unneeded here as no-one can have accessed this
+ * pointer yet, barring us */
+ p->cred = pcred;
+ return 0;
+}
diff --git a/kernel/fork.c b/kernel/fork.c
index 81fdc773390..c932e283ddf 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -146,9 +146,7 @@ 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_cred.user);
- put_group_info(tsk->__temp_cred.group_info);
+ put_cred(tsk->cred);
delayacct_tsk_free(tsk);
if (!profile_handoff_task(tsk))
@@ -969,7 +967,6 @@ 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->cred = &p->__temp_cred;
retval = -EAGAIN;
if (atomic_read(&p->cred->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
@@ -978,9 +975,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
goto bad_fork_free;
}
- atomic_inc(&p->cred->user->__count);
- atomic_inc(&p->cred->user->processes);
- get_group_info(p->cred->group_info);
+ retval = copy_creds(p, clone_flags);
+ if (retval < 0)
+ goto bad_fork_free;
/*
* If multiple threads are within copy_process(), then this check
@@ -1035,9 +1032,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->cred->security = NULL;
-#endif
p->io_context = NULL;
p->audit_context = NULL;
cgroup_fork(p);
@@ -1082,10 +1076,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
/* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p, 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;
@@ -1284,8 +1276,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_put(p->mempolicy);
@@ -1298,9 +1288,7 @@ 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->cred->group_info);
- atomic_dec(&p->cred->user->processes);
- free_uid(p->cred->user);
+ put_cred(p->cred);
bad_fork_free:
free_task(p);
fork_out: