diff options
| author | David Howells <dhowells@redhat.com> | 2008-11-13 18:39:17 -0500 |
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2008-11-13 18:39:17 -0500 |
| commit | f1752eec6145c97163dbce62d17cf5d928e28a27 (patch) | |
| tree | 16bc51166d38815092de36a461b845b0b4b522f9 /kernel | |
| parent | b6dff3ec5e116e3af6f537d4caedcad6b9e5082a (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/Makefile | 2 | ||||
| -rw-r--r-- | kernel/cred.c | 96 | ||||
| -rw-r--r-- | kernel/fork.c | 24 |
3 files changed, 103 insertions, 19 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index 9a3ec66a9d84..5a6a612c302d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
| @@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ | |||
| 9 | rcupdate.o extable.o params.o posix-timers.o \ | 9 | rcupdate.o extable.o params.o posix-timers.o \ |
| 10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ | 10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ |
| 11 | hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ | 11 | hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ |
| 12 | notifier.o ksysfs.o pm_qos_params.o sched_clock.o | 12 | notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o |
| 13 | 13 | ||
| 14 | CFLAGS_REMOVE_sched.o = -mno-spe | 14 | CFLAGS_REMOVE_sched.o = -mno-spe |
| 15 | 15 | ||
diff --git a/kernel/cred.c b/kernel/cred.c new file mode 100644 index 000000000000..833244a7cb05 --- /dev/null +++ b/kernel/cred.c | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* Task credentials management | ||
| 2 | * | ||
| 3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public Licence | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the Licence, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/cred.h> | ||
| 13 | #include <linux/sched.h> | ||
| 14 | #include <linux/key.h> | ||
| 15 | #include <linux/keyctl.h> | ||
| 16 | #include <linux/init_task.h> | ||
| 17 | #include <linux/security.h> | ||
| 18 | |||
| 19 | /* | ||
| 20 | * The initial credentials for the initial task | ||
| 21 | */ | ||
| 22 | struct cred init_cred = { | ||
| 23 | .usage = ATOMIC_INIT(3), | ||
| 24 | .securebits = SECUREBITS_DEFAULT, | ||
| 25 | .cap_inheritable = CAP_INIT_INH_SET, | ||
| 26 | .cap_permitted = CAP_FULL_SET, | ||
| 27 | .cap_effective = CAP_INIT_EFF_SET, | ||
| 28 | .cap_bset = CAP_INIT_BSET, | ||
| 29 | .user = INIT_USER, | ||
| 30 | .group_info = &init_groups, | ||
| 31 | }; | ||
| 32 | |||
| 33 | /* | ||
| 34 | * The RCU callback to actually dispose of a set of credentials | ||
| 35 | */ | ||
| 36 | static void put_cred_rcu(struct rcu_head *rcu) | ||
| 37 | { | ||
| 38 | struct cred *cred = container_of(rcu, struct cred, rcu); | ||
| 39 | |||
| 40 | BUG_ON(atomic_read(&cred->usage) != 0); | ||
| 41 | |||
| 42 | key_put(cred->thread_keyring); | ||
| 43 | key_put(cred->request_key_auth); | ||
| 44 | put_group_info(cred->group_info); | ||
| 45 | free_uid(cred->user); | ||
| 46 | security_cred_free(cred); | ||
| 47 | kfree(cred); | ||
| 48 | } | ||
| 49 | |||
| 50 | /** | ||
| 51 | * __put_cred - Destroy a set of credentials | ||
| 52 | * @sec: The record to release | ||
| 53 | * | ||
| 54 | * Destroy a set of credentials on which no references remain. | ||
| 55 | */ | ||
| 56 | void __put_cred(struct cred *cred) | ||
| 57 | { | ||
| 58 | call_rcu(&cred->rcu, put_cred_rcu); | ||
| 59 | } | ||
| 60 | EXPORT_SYMBOL(__put_cred); | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Copy credentials for the new process created by fork() | ||
| 64 | */ | ||
| 65 | int copy_creds(struct task_struct *p, unsigned long clone_flags) | ||
| 66 | { | ||
| 67 | struct cred *pcred; | ||
| 68 | int ret; | ||
| 69 | |||
| 70 | pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL); | ||
| 71 | if (!pcred) | ||
| 72 | return -ENOMEM; | ||
| 73 | |||
| 74 | #ifdef CONFIG_SECURITY | ||
| 75 | pcred->security = NULL; | ||
| 76 | #endif | ||
| 77 | |||
| 78 | ret = security_cred_alloc(pcred); | ||
| 79 | if (ret < 0) { | ||
| 80 | kfree(pcred); | ||
| 81 | return ret; | ||
| 82 | } | ||
| 83 | |||
| 84 | atomic_set(&pcred->usage, 1); | ||
| 85 | get_group_info(pcred->group_info); | ||
| 86 | get_uid(pcred->user); | ||
| 87 | key_get(pcred->thread_keyring); | ||
| 88 | key_get(pcred->request_key_auth); | ||
| 89 | |||
| 90 | atomic_inc(&pcred->user->processes); | ||
| 91 | |||
| 92 | /* RCU assignment is unneeded here as no-one can have accessed this | ||
| 93 | * pointer yet, barring us */ | ||
| 94 | p->cred = pcred; | ||
| 95 | return 0; | ||
| 96 | } | ||
diff --git a/kernel/fork.c b/kernel/fork.c index 81fdc7733908..c932e283ddfc 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -146,9 +146,7 @@ void __put_task_struct(struct task_struct *tsk) | |||
| 146 | WARN_ON(atomic_read(&tsk->usage)); | 146 | WARN_ON(atomic_read(&tsk->usage)); |
| 147 | WARN_ON(tsk == current); | 147 | WARN_ON(tsk == current); |
| 148 | 148 | ||
| 149 | security_task_free(tsk); | 149 | put_cred(tsk->cred); |
| 150 | free_uid(tsk->__temp_cred.user); | ||
| 151 | put_group_info(tsk->__temp_cred.group_info); | ||
| 152 | delayacct_tsk_free(tsk); | 150 | delayacct_tsk_free(tsk); |
| 153 | 151 | ||
| 154 | if (!profile_handoff_task(tsk)) | 152 | if (!profile_handoff_task(tsk)) |
| @@ -969,7 +967,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 969 | DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); | 967 | DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); |
| 970 | DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); | 968 | DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); |
| 971 | #endif | 969 | #endif |
| 972 | p->cred = &p->__temp_cred; | ||
| 973 | retval = -EAGAIN; | 970 | retval = -EAGAIN; |
| 974 | if (atomic_read(&p->cred->user->processes) >= | 971 | if (atomic_read(&p->cred->user->processes) >= |
| 975 | p->signal->rlim[RLIMIT_NPROC].rlim_cur) { | 972 | p->signal->rlim[RLIMIT_NPROC].rlim_cur) { |
| @@ -978,9 +975,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 978 | goto bad_fork_free; | 975 | goto bad_fork_free; |
| 979 | } | 976 | } |
| 980 | 977 | ||
| 981 | atomic_inc(&p->cred->user->__count); | 978 | retval = copy_creds(p, clone_flags); |
| 982 | atomic_inc(&p->cred->user->processes); | 979 | if (retval < 0) |
| 983 | get_group_info(p->cred->group_info); | 980 | goto bad_fork_free; |
| 984 | 981 | ||
| 985 | /* | 982 | /* |
| 986 | * If multiple threads are within copy_process(), then this check | 983 | * If multiple threads are within copy_process(), then this check |
| @@ -1035,9 +1032,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 1035 | do_posix_clock_monotonic_gettime(&p->start_time); | 1032 | do_posix_clock_monotonic_gettime(&p->start_time); |
| 1036 | p->real_start_time = p->start_time; | 1033 | p->real_start_time = p->start_time; |
| 1037 | monotonic_to_bootbased(&p->real_start_time); | 1034 | monotonic_to_bootbased(&p->real_start_time); |
| 1038 | #ifdef CONFIG_SECURITY | ||
| 1039 | p->cred->security = NULL; | ||
| 1040 | #endif | ||
| 1041 | p->io_context = NULL; | 1035 | p->io_context = NULL; |
| 1042 | p->audit_context = NULL; | 1036 | p->audit_context = NULL; |
| 1043 | cgroup_fork(p); | 1037 | cgroup_fork(p); |
| @@ -1082,10 +1076,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 1082 | /* Perform scheduler related setup. Assign this task to a CPU. */ | 1076 | /* Perform scheduler related setup. Assign this task to a CPU. */ |
| 1083 | sched_fork(p, clone_flags); | 1077 | sched_fork(p, clone_flags); |
| 1084 | 1078 | ||
| 1085 | if ((retval = security_task_alloc(p))) | ||
| 1086 | goto bad_fork_cleanup_policy; | ||
| 1087 | if ((retval = audit_alloc(p))) | 1079 | if ((retval = audit_alloc(p))) |
| 1088 | goto bad_fork_cleanup_security; | 1080 | goto bad_fork_cleanup_policy; |
| 1089 | /* copy all the process information */ | 1081 | /* copy all the process information */ |
| 1090 | if ((retval = copy_semundo(clone_flags, p))) | 1082 | if ((retval = copy_semundo(clone_flags, p))) |
| 1091 | goto bad_fork_cleanup_audit; | 1083 | goto bad_fork_cleanup_audit; |
| @@ -1284,8 +1276,6 @@ bad_fork_cleanup_semundo: | |||
| 1284 | exit_sem(p); | 1276 | exit_sem(p); |
| 1285 | bad_fork_cleanup_audit: | 1277 | bad_fork_cleanup_audit: |
| 1286 | audit_free(p); | 1278 | audit_free(p); |
| 1287 | bad_fork_cleanup_security: | ||
| 1288 | security_task_free(p); | ||
| 1289 | bad_fork_cleanup_policy: | 1279 | bad_fork_cleanup_policy: |
| 1290 | #ifdef CONFIG_NUMA | 1280 | #ifdef CONFIG_NUMA |
| 1291 | mpol_put(p->mempolicy); | 1281 | mpol_put(p->mempolicy); |
| @@ -1298,9 +1288,7 @@ bad_fork_cleanup_cgroup: | |||
| 1298 | bad_fork_cleanup_put_domain: | 1288 | bad_fork_cleanup_put_domain: |
| 1299 | module_put(task_thread_info(p)->exec_domain->module); | 1289 | module_put(task_thread_info(p)->exec_domain->module); |
| 1300 | bad_fork_cleanup_count: | 1290 | bad_fork_cleanup_count: |
| 1301 | put_group_info(p->cred->group_info); | 1291 | put_cred(p->cred); |
| 1302 | atomic_dec(&p->cred->user->processes); | ||
| 1303 | free_uid(p->cred->user); | ||
| 1304 | bad_fork_free: | 1292 | bad_fork_free: |
| 1305 | free_task(p); | 1293 | free_task(p); |
| 1306 | fork_out: | 1294 | fork_out: |
