diff options
Diffstat (limited to 'kernel/cred.c')
-rw-r--r-- | kernel/cred.c | 321 |
1 files changed, 284 insertions, 37 deletions
diff --git a/kernel/cred.c b/kernel/cred.c index ac73e3617684..cb6b5eda978d 100644 --- a/kernel/cred.c +++ b/kernel/cred.c | |||
@@ -15,6 +15,10 @@ | |||
15 | #include <linux/keyctl.h> | 15 | #include <linux/keyctl.h> |
16 | #include <linux/init_task.h> | 16 | #include <linux/init_task.h> |
17 | #include <linux/security.h> | 17 | #include <linux/security.h> |
18 | #include <linux/cn_proc.h> | ||
19 | #include "cred-internals.h" | ||
20 | |||
21 | static struct kmem_cache *cred_jar; | ||
18 | 22 | ||
19 | /* | 23 | /* |
20 | * The common credentials for the initial task's thread group | 24 | * The common credentials for the initial task's thread group |
@@ -64,7 +68,7 @@ static void release_tgcred_rcu(struct rcu_head *rcu) | |||
64 | /* | 68 | /* |
65 | * Release a set of thread group credentials. | 69 | * Release a set of thread group credentials. |
66 | */ | 70 | */ |
67 | static void release_tgcred(struct cred *cred) | 71 | void release_tgcred(struct cred *cred) |
68 | { | 72 | { |
69 | #ifdef CONFIG_KEYS | 73 | #ifdef CONFIG_KEYS |
70 | struct thread_group_cred *tgcred = cred->tgcred; | 74 | struct thread_group_cred *tgcred = cred->tgcred; |
@@ -81,79 +85,322 @@ static void put_cred_rcu(struct rcu_head *rcu) | |||
81 | { | 85 | { |
82 | struct cred *cred = container_of(rcu, struct cred, rcu); | 86 | struct cred *cred = container_of(rcu, struct cred, rcu); |
83 | 87 | ||
84 | BUG_ON(atomic_read(&cred->usage) != 0); | 88 | if (atomic_read(&cred->usage) != 0) |
89 | panic("CRED: put_cred_rcu() sees %p with usage %d\n", | ||
90 | cred, atomic_read(&cred->usage)); | ||
85 | 91 | ||
92 | security_cred_free(cred); | ||
86 | key_put(cred->thread_keyring); | 93 | key_put(cred->thread_keyring); |
87 | key_put(cred->request_key_auth); | 94 | key_put(cred->request_key_auth); |
88 | release_tgcred(cred); | 95 | release_tgcred(cred); |
89 | put_group_info(cred->group_info); | 96 | put_group_info(cred->group_info); |
90 | free_uid(cred->user); | 97 | free_uid(cred->user); |
91 | security_cred_free(cred); | 98 | kmem_cache_free(cred_jar, cred); |
92 | kfree(cred); | ||
93 | } | 99 | } |
94 | 100 | ||
95 | /** | 101 | /** |
96 | * __put_cred - Destroy a set of credentials | 102 | * __put_cred - Destroy a set of credentials |
97 | * @sec: The record to release | 103 | * @cred: The record to release |
98 | * | 104 | * |
99 | * Destroy a set of credentials on which no references remain. | 105 | * Destroy a set of credentials on which no references remain. |
100 | */ | 106 | */ |
101 | void __put_cred(struct cred *cred) | 107 | void __put_cred(struct cred *cred) |
102 | { | 108 | { |
109 | BUG_ON(atomic_read(&cred->usage) != 0); | ||
110 | |||
103 | call_rcu(&cred->rcu, put_cred_rcu); | 111 | call_rcu(&cred->rcu, put_cred_rcu); |
104 | } | 112 | } |
105 | EXPORT_SYMBOL(__put_cred); | 113 | EXPORT_SYMBOL(__put_cred); |
106 | 114 | ||
115 | /** | ||
116 | * prepare_creds - Prepare a new set of credentials for modification | ||
117 | * | ||
118 | * Prepare a new set of task credentials for modification. A task's creds | ||
119 | * shouldn't generally be modified directly, therefore this function is used to | ||
120 | * prepare a new copy, which the caller then modifies and then commits by | ||
121 | * calling commit_creds(). | ||
122 | * | ||
123 | * Returns a pointer to the new creds-to-be if successful, NULL otherwise. | ||
124 | * | ||
125 | * Call commit_creds() or abort_creds() to clean up. | ||
126 | */ | ||
127 | struct cred *prepare_creds(void) | ||
128 | { | ||
129 | struct task_struct *task = current; | ||
130 | const struct cred *old; | ||
131 | struct cred *new; | ||
132 | |||
133 | BUG_ON(atomic_read(&task->cred->usage) < 1); | ||
134 | |||
135 | new = kmem_cache_alloc(cred_jar, GFP_KERNEL); | ||
136 | if (!new) | ||
137 | return NULL; | ||
138 | |||
139 | old = task->cred; | ||
140 | memcpy(new, old, sizeof(struct cred)); | ||
141 | |||
142 | atomic_set(&new->usage, 1); | ||
143 | get_group_info(new->group_info); | ||
144 | get_uid(new->user); | ||
145 | |||
146 | #ifdef CONFIG_KEYS | ||
147 | key_get(new->thread_keyring); | ||
148 | key_get(new->request_key_auth); | ||
149 | atomic_inc(&new->tgcred->usage); | ||
150 | #endif | ||
151 | |||
152 | #ifdef CONFIG_SECURITY | ||
153 | new->security = NULL; | ||
154 | #endif | ||
155 | |||
156 | if (security_prepare_creds(new, old, GFP_KERNEL) < 0) | ||
157 | goto error; | ||
158 | return new; | ||
159 | |||
160 | error: | ||
161 | abort_creds(new); | ||
162 | return NULL; | ||
163 | } | ||
164 | EXPORT_SYMBOL(prepare_creds); | ||
165 | |||
166 | /* | ||
167 | * prepare new credentials for the usermode helper dispatcher | ||
168 | */ | ||
169 | struct cred *prepare_usermodehelper_creds(void) | ||
170 | { | ||
171 | #ifdef CONFIG_KEYS | ||
172 | struct thread_group_cred *tgcred = NULL; | ||
173 | #endif | ||
174 | struct cred *new; | ||
175 | |||
176 | #ifdef CONFIG_KEYS | ||
177 | tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC); | ||
178 | if (!tgcred) | ||
179 | return NULL; | ||
180 | #endif | ||
181 | |||
182 | new = kmem_cache_alloc(cred_jar, GFP_ATOMIC); | ||
183 | if (!new) | ||
184 | return NULL; | ||
185 | |||
186 | memcpy(new, &init_cred, sizeof(struct cred)); | ||
187 | |||
188 | atomic_set(&new->usage, 1); | ||
189 | get_group_info(new->group_info); | ||
190 | get_uid(new->user); | ||
191 | |||
192 | #ifdef CONFIG_KEYS | ||
193 | new->thread_keyring = NULL; | ||
194 | new->request_key_auth = NULL; | ||
195 | new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT; | ||
196 | |||
197 | atomic_set(&tgcred->usage, 1); | ||
198 | spin_lock_init(&tgcred->lock); | ||
199 | new->tgcred = tgcred; | ||
200 | #endif | ||
201 | |||
202 | #ifdef CONFIG_SECURITY | ||
203 | new->security = NULL; | ||
204 | #endif | ||
205 | if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0) | ||
206 | goto error; | ||
207 | |||
208 | BUG_ON(atomic_read(&new->usage) != 1); | ||
209 | return new; | ||
210 | |||
211 | error: | ||
212 | put_cred(new); | ||
213 | return NULL; | ||
214 | } | ||
215 | |||
107 | /* | 216 | /* |
108 | * Copy credentials for the new process created by fork() | 217 | * Copy credentials for the new process created by fork() |
218 | * | ||
219 | * We share if we can, but under some circumstances we have to generate a new | ||
220 | * set. | ||
109 | */ | 221 | */ |
110 | int copy_creds(struct task_struct *p, unsigned long clone_flags) | 222 | int copy_creds(struct task_struct *p, unsigned long clone_flags) |
111 | { | 223 | { |
112 | struct cred *pcred; | 224 | #ifdef CONFIG_KEYS |
113 | int ret; | 225 | struct thread_group_cred *tgcred; |
226 | #endif | ||
227 | struct cred *new; | ||
228 | |||
229 | mutex_init(&p->cred_exec_mutex); | ||
114 | 230 | ||
115 | pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL); | 231 | if ( |
116 | if (!pcred) | 232 | #ifdef CONFIG_KEYS |
233 | !p->cred->thread_keyring && | ||
234 | #endif | ||
235 | clone_flags & CLONE_THREAD | ||
236 | ) { | ||
237 | get_cred(p->cred); | ||
238 | atomic_inc(&p->cred->user->processes); | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | new = prepare_creds(); | ||
243 | if (!new) | ||
117 | return -ENOMEM; | 244 | return -ENOMEM; |
118 | 245 | ||
119 | #ifdef CONFIG_KEYS | 246 | #ifdef CONFIG_KEYS |
120 | if (clone_flags & CLONE_THREAD) { | 247 | /* new threads get their own thread keyrings if their parent already |
121 | atomic_inc(&pcred->tgcred->usage); | 248 | * had one */ |
122 | } else { | 249 | if (new->thread_keyring) { |
123 | pcred->tgcred = kmalloc(sizeof(struct cred), GFP_KERNEL); | 250 | key_put(new->thread_keyring); |
124 | if (!pcred->tgcred) { | 251 | new->thread_keyring = NULL; |
125 | kfree(pcred); | 252 | if (clone_flags & CLONE_THREAD) |
253 | install_thread_keyring_to_cred(new); | ||
254 | } | ||
255 | |||
256 | /* we share the process and session keyrings between all the threads in | ||
257 | * a process - this is slightly icky as we violate COW credentials a | ||
258 | * bit */ | ||
259 | if (!(clone_flags & CLONE_THREAD)) { | ||
260 | tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); | ||
261 | if (!tgcred) { | ||
262 | put_cred(new); | ||
126 | return -ENOMEM; | 263 | return -ENOMEM; |
127 | } | 264 | } |
128 | atomic_set(&pcred->tgcred->usage, 1); | 265 | atomic_set(&tgcred->usage, 1); |
129 | spin_lock_init(&pcred->tgcred->lock); | 266 | spin_lock_init(&tgcred->lock); |
130 | pcred->tgcred->process_keyring = NULL; | 267 | tgcred->process_keyring = NULL; |
131 | pcred->tgcred->session_keyring = | 268 | tgcred->session_keyring = key_get(new->tgcred->session_keyring); |
132 | key_get(p->cred->tgcred->session_keyring); | 269 | |
270 | release_tgcred(new); | ||
271 | new->tgcred = tgcred; | ||
133 | } | 272 | } |
134 | #endif | 273 | #endif |
135 | 274 | ||
136 | #ifdef CONFIG_SECURITY | 275 | atomic_inc(&new->user->processes); |
137 | pcred->security = NULL; | 276 | p->cred = new; |
138 | #endif | 277 | return 0; |
278 | } | ||
139 | 279 | ||
140 | ret = security_cred_alloc(pcred); | 280 | /** |
141 | if (ret < 0) { | 281 | * commit_creds - Install new credentials upon the current task |
142 | release_tgcred(pcred); | 282 | * @new: The credentials to be assigned |
143 | kfree(pcred); | 283 | * |
144 | return ret; | 284 | * Install a new set of credentials to the current task, using RCU to replace |
285 | * the old set. | ||
286 | * | ||
287 | * This function eats the caller's reference to the new credentials. | ||
288 | * | ||
289 | * Always returns 0 thus allowing this function to be tail-called at the end | ||
290 | * of, say, sys_setgid(). | ||
291 | */ | ||
292 | int commit_creds(struct cred *new) | ||
293 | { | ||
294 | struct task_struct *task = current; | ||
295 | const struct cred *old; | ||
296 | |||
297 | BUG_ON(atomic_read(&new->usage) < 1); | ||
298 | BUG_ON(atomic_read(&task->cred->usage) < 1); | ||
299 | |||
300 | old = task->cred; | ||
301 | security_commit_creds(new, old); | ||
302 | |||
303 | /* dumpability changes */ | ||
304 | if (old->euid != new->euid || | ||
305 | old->egid != new->egid || | ||
306 | old->fsuid != new->fsuid || | ||
307 | old->fsgid != new->fsgid || | ||
308 | !cap_issubset(new->cap_permitted, old->cap_permitted)) { | ||
309 | set_dumpable(task->mm, suid_dumpable); | ||
310 | task->pdeath_signal = 0; | ||
311 | smp_wmb(); | ||
145 | } | 312 | } |
146 | 313 | ||
147 | atomic_set(&pcred->usage, 1); | 314 | /* alter the thread keyring */ |
148 | get_group_info(pcred->group_info); | 315 | if (new->fsuid != old->fsuid) |
149 | get_uid(pcred->user); | 316 | key_fsuid_changed(task); |
150 | key_get(pcred->thread_keyring); | 317 | if (new->fsgid != old->fsgid) |
151 | key_get(pcred->request_key_auth); | 318 | key_fsgid_changed(task); |
319 | |||
320 | /* do it | ||
321 | * - What if a process setreuid()'s and this brings the | ||
322 | * new uid over his NPROC rlimit? We can check this now | ||
323 | * cheaply with the new uid cache, so if it matters | ||
324 | * we should be checking for it. -DaveM | ||
325 | */ | ||
326 | if (new->user != old->user) | ||
327 | atomic_inc(&new->user->processes); | ||
328 | rcu_assign_pointer(task->cred, new); | ||
329 | if (new->user != old->user) | ||
330 | atomic_dec(&old->user->processes); | ||
331 | |||
332 | sched_switch_user(task); | ||
333 | |||
334 | /* send notifications */ | ||
335 | if (new->uid != old->uid || | ||
336 | new->euid != old->euid || | ||
337 | new->suid != old->suid || | ||
338 | new->fsuid != old->fsuid) | ||
339 | proc_id_connector(task, PROC_EVENT_UID); | ||
152 | 340 | ||
153 | atomic_inc(&pcred->user->processes); | 341 | if (new->gid != old->gid || |
342 | new->egid != old->egid || | ||
343 | new->sgid != old->sgid || | ||
344 | new->fsgid != old->fsgid) | ||
345 | proc_id_connector(task, PROC_EVENT_GID); | ||
154 | 346 | ||
155 | /* RCU assignment is unneeded here as no-one can have accessed this | 347 | put_cred(old); |
156 | * pointer yet, barring us */ | ||
157 | p->cred = pcred; | ||
158 | return 0; | 348 | return 0; |
159 | } | 349 | } |
350 | EXPORT_SYMBOL(commit_creds); | ||
351 | |||
352 | /** | ||
353 | * abort_creds - Discard a set of credentials and unlock the current task | ||
354 | * @new: The credentials that were going to be applied | ||
355 | * | ||
356 | * Discard a set of credentials that were under construction and unlock the | ||
357 | * current task. | ||
358 | */ | ||
359 | void abort_creds(struct cred *new) | ||
360 | { | ||
361 | BUG_ON(atomic_read(&new->usage) < 1); | ||
362 | put_cred(new); | ||
363 | } | ||
364 | EXPORT_SYMBOL(abort_creds); | ||
365 | |||
366 | /** | ||
367 | * override_creds - Temporarily override the current process's credentials | ||
368 | * @new: The credentials to be assigned | ||
369 | * | ||
370 | * Install a set of temporary override credentials on the current process, | ||
371 | * returning the old set for later reversion. | ||
372 | */ | ||
373 | const struct cred *override_creds(const struct cred *new) | ||
374 | { | ||
375 | const struct cred *old = current->cred; | ||
376 | |||
377 | rcu_assign_pointer(current->cred, get_cred(new)); | ||
378 | return old; | ||
379 | } | ||
380 | EXPORT_SYMBOL(override_creds); | ||
381 | |||
382 | /** | ||
383 | * revert_creds - Revert a temporary credentials override | ||
384 | * @old: The credentials to be restored | ||
385 | * | ||
386 | * Revert a temporary set of override credentials to an old set, discarding the | ||
387 | * override set. | ||
388 | */ | ||
389 | void revert_creds(const struct cred *old) | ||
390 | { | ||
391 | const struct cred *override = current->cred; | ||
392 | |||
393 | rcu_assign_pointer(current->cred, old); | ||
394 | put_cred(override); | ||
395 | } | ||
396 | EXPORT_SYMBOL(revert_creds); | ||
397 | |||
398 | /* | ||
399 | * initialise the credentials stuff | ||
400 | */ | ||
401 | void __init cred_init(void) | ||
402 | { | ||
403 | /* allocate a slab in which we can store credentials */ | ||
404 | cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred), | ||
405 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); | ||
406 | } | ||