CRED: Separate per-task-group keyrings from signal_struct
[linux-3.10.git] / kernel / cred.c
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 common credentials for the initial task's thread group
21  */
22 #ifdef CONFIG_KEYS
23 static struct thread_group_cred init_tgcred = {
24         .usage  = ATOMIC_INIT(2),
25         .tgid   = 0,
26         .lock   = SPIN_LOCK_UNLOCKED,
27 };
28 #endif
29
30 /*
31  * The initial credentials for the initial task
32  */
33 struct cred init_cred = {
34         .usage                  = ATOMIC_INIT(3),
35         .securebits             = SECUREBITS_DEFAULT,
36         .cap_inheritable        = CAP_INIT_INH_SET,
37         .cap_permitted          = CAP_FULL_SET,
38         .cap_effective          = CAP_INIT_EFF_SET,
39         .cap_bset               = CAP_INIT_BSET,
40         .user                   = INIT_USER,
41         .group_info             = &init_groups,
42 #ifdef CONFIG_KEYS
43         .tgcred                 = &init_tgcred,
44 #endif
45 };
46
47 /*
48  * Dispose of the shared task group credentials
49  */
50 #ifdef CONFIG_KEYS
51 static void release_tgcred_rcu(struct rcu_head *rcu)
52 {
53         struct thread_group_cred *tgcred =
54                 container_of(rcu, struct thread_group_cred, rcu);
55
56         BUG_ON(atomic_read(&tgcred->usage) != 0);
57
58         key_put(tgcred->session_keyring);
59         key_put(tgcred->process_keyring);
60         kfree(tgcred);
61 }
62 #endif
63
64 /*
65  * Release a set of thread group credentials.
66  */
67 static void release_tgcred(struct cred *cred)
68 {
69 #ifdef CONFIG_KEYS
70         struct thread_group_cred *tgcred = cred->tgcred;
71
72         if (atomic_dec_and_test(&tgcred->usage))
73                 call_rcu(&tgcred->rcu, release_tgcred_rcu);
74 #endif
75 }
76
77 /*
78  * The RCU callback to actually dispose of a set of credentials
79  */
80 static void put_cred_rcu(struct rcu_head *rcu)
81 {
82         struct cred *cred = container_of(rcu, struct cred, rcu);
83
84         BUG_ON(atomic_read(&cred->usage) != 0);
85
86         key_put(cred->thread_keyring);
87         key_put(cred->request_key_auth);
88         release_tgcred(cred);
89         put_group_info(cred->group_info);
90         free_uid(cred->user);
91         security_cred_free(cred);
92         kfree(cred);
93 }
94
95 /**
96  * __put_cred - Destroy a set of credentials
97  * @sec: The record to release
98  *
99  * Destroy a set of credentials on which no references remain.
100  */
101 void __put_cred(struct cred *cred)
102 {
103         call_rcu(&cred->rcu, put_cred_rcu);
104 }
105 EXPORT_SYMBOL(__put_cred);
106
107 /*
108  * Copy credentials for the new process created by fork()
109  */
110 int copy_creds(struct task_struct *p, unsigned long clone_flags)
111 {
112         struct cred *pcred;
113         int ret;
114
115         pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL);
116         if (!pcred)
117                 return -ENOMEM;
118
119 #ifdef CONFIG_KEYS
120         if (clone_flags & CLONE_THREAD) {
121                 atomic_inc(&pcred->tgcred->usage);
122         } else {
123                 pcred->tgcred = kmalloc(sizeof(struct cred), GFP_KERNEL);
124                 if (!pcred->tgcred) {
125                         kfree(pcred);
126                         return -ENOMEM;
127                 }
128                 atomic_set(&pcred->tgcred->usage, 1);
129                 spin_lock_init(&pcred->tgcred->lock);
130                 pcred->tgcred->process_keyring = NULL;
131                 pcred->tgcred->session_keyring =
132                         key_get(p->cred->tgcred->session_keyring);
133         }
134 #endif
135
136 #ifdef CONFIG_SECURITY
137         pcred->security = NULL;
138 #endif
139
140         ret = security_cred_alloc(pcred);
141         if (ret < 0) {
142                 release_tgcred(pcred);
143                 kfree(pcred);
144                 return ret;
145         }
146
147         atomic_set(&pcred->usage, 1);
148         get_group_info(pcred->group_info);
149         get_uid(pcred->user);
150         key_get(pcred->thread_keyring);
151         key_get(pcred->request_key_auth);
152
153         atomic_inc(&pcred->user->processes);
154
155         /* RCU assignment is unneeded here as no-one can have accessed this
156          * pointer yet, barring us */
157         p->cred = pcred;
158         return 0;
159 }