blob: 45b240af6dbe2cd1a9dd22b665e60590e0066a15 [file] [log] [blame]
David Howells69664cf2008-04-29 01:01:31 -07001/* Management of a process's keyrings
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
David Howells69664cf2008-04-29 01:01:31 -07003 * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * 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 License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/sched.h>
15#include <linux/slab.h>
16#include <linux/keyctl.h>
17#include <linux/fs.h>
18#include <linux/err.h>
Ingo Molnarbb003072006-03-22 00:09:14 -080019#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <asm/uaccess.h>
21#include "internal.h"
22
23/* session keyring create vs join semaphore */
Ingo Molnarbb003072006-03-22 00:09:14 -080024static DEFINE_MUTEX(key_session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
David Howells69664cf2008-04-29 01:01:31 -070026/* user keyring creation semaphore */
27static DEFINE_MUTEX(key_user_keyring_mutex);
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029/* the root user's tracking struct */
30struct key_user root_key_user = {
31 .usage = ATOMIC_INIT(3),
David Howells76181c12007-10-16 23:29:46 -070032 .cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock),
Peter Zijlstra6cfd76a2006-12-06 20:37:22 -080033 .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 .nkeys = ATOMIC_INIT(2),
35 .nikeys = ATOMIC_INIT(2),
36 .uid = 0,
37};
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039/*****************************************************************************/
40/*
David Howells69664cf2008-04-29 01:01:31 -070041 * install user and user session keyrings for a particular UID
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 */
David Howells1f8f5cf2008-11-10 19:00:05 +000043int install_user_keyrings(struct task_struct *tsk)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
David Howells69664cf2008-04-29 01:01:31 -070045 struct user_struct *user = tsk->user;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 struct key *uid_keyring, *session_keyring;
47 char buf[20];
48 int ret;
49
David Howells69664cf2008-04-29 01:01:31 -070050 kenter("%p{%u}", user, user->uid);
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
David Howells69664cf2008-04-29 01:01:31 -070052 if (user->uid_keyring) {
53 kleave(" = 0 [exist]");
54 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 }
56
David Howells69664cf2008-04-29 01:01:31 -070057 mutex_lock(&key_user_keyring_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 ret = 0;
59
David Howells69664cf2008-04-29 01:01:31 -070060 if (!user->uid_keyring) {
61 /* get the UID-specific keyring
62 * - there may be one in existence already as it may have been
63 * pinned by a session, but the user_struct pointing to it
64 * may have been destroyed by setuid */
65 sprintf(buf, "_uid.%u", user->uid);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
David Howells69664cf2008-04-29 01:01:31 -070067 uid_keyring = find_keyring_by_name(buf, true);
68 if (IS_ERR(uid_keyring)) {
69 uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
70 tsk, KEY_ALLOC_IN_QUOTA,
71 NULL);
72 if (IS_ERR(uid_keyring)) {
73 ret = PTR_ERR(uid_keyring);
74 goto error;
75 }
76 }
77
78 /* get a default session keyring (which might also exist
79 * already) */
80 sprintf(buf, "_uid_ses.%u", user->uid);
81
82 session_keyring = find_keyring_by_name(buf, true);
83 if (IS_ERR(session_keyring)) {
84 session_keyring =
85 keyring_alloc(buf, user->uid, (gid_t) -1,
86 tsk, KEY_ALLOC_IN_QUOTA, NULL);
87 if (IS_ERR(session_keyring)) {
88 ret = PTR_ERR(session_keyring);
89 goto error_release;
90 }
91
92 /* we install a link from the user session keyring to
93 * the user keyring */
94 ret = key_link(session_keyring, uid_keyring);
95 if (ret < 0)
96 goto error_release_both;
97 }
98
99 /* install the keyrings */
100 user->uid_keyring = uid_keyring;
101 user->session_keyring = session_keyring;
102 }
103
104 mutex_unlock(&key_user_keyring_mutex);
105 kleave(" = 0");
106 return 0;
107
108error_release_both:
109 key_put(session_keyring);
110error_release:
111 key_put(uid_keyring);
112error:
113 mutex_unlock(&key_user_keyring_mutex);
114 kleave(" = %d", ret);
115 return ret;
116}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118/*****************************************************************************/
119/*
120 * deal with the UID changing
121 */
122void switch_uid_keyring(struct user_struct *new_user)
123{
124#if 0 /* do nothing for now */
125 struct key *old;
126
127 /* switch to the new user's session keyring if we were running under
128 * root's default session keyring */
129 if (new_user->uid != 0 &&
130 current->session_keyring == &root_session_keyring
131 ) {
132 atomic_inc(&new_user->session_keyring->usage);
133
134 task_lock(current);
135 old = current->session_keyring;
136 current->session_keyring = new_user->session_keyring;
137 task_unlock(current);
138
139 key_put(old);
140 }
141#endif
142
143} /* end switch_uid_keyring() */
144
145/*****************************************************************************/
146/*
147 * install a fresh thread keyring, discarding the old one
148 */
149int install_thread_keyring(struct task_struct *tsk)
150{
151 struct key *keyring, *old;
152 char buf[20];
153 int ret;
154
155 sprintf(buf, "_tid.%u", tsk->pid);
156
David Howells7e047ef2006-06-26 00:24:50 -0700157 keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
158 KEY_ALLOC_QUOTA_OVERRUN, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 if (IS_ERR(keyring)) {
160 ret = PTR_ERR(keyring);
161 goto error;
162 }
163
164 task_lock(tsk);
165 old = tsk->thread_keyring;
166 tsk->thread_keyring = keyring;
167 task_unlock(tsk);
168
169 ret = 0;
170
171 key_put(old);
David Howells664cceb2005-09-28 17:03:15 +0100172error:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 return ret;
174
175} /* end install_thread_keyring() */
176
177/*****************************************************************************/
178/*
179 * make sure a process keyring is installed
180 */
David Howells3e301482005-06-23 22:00:56 -0700181int install_process_keyring(struct task_struct *tsk)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 struct key *keyring;
184 char buf[20];
185 int ret;
186
David Howells1a26feb2006-04-10 22:54:26 -0700187 might_sleep();
188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 if (!tsk->signal->process_keyring) {
190 sprintf(buf, "_pid.%u", tsk->tgid);
191
David Howells7e047ef2006-06-26 00:24:50 -0700192 keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
193 KEY_ALLOC_QUOTA_OVERRUN, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 if (IS_ERR(keyring)) {
195 ret = PTR_ERR(keyring);
196 goto error;
197 }
198
David Howells8589b4e2005-06-23 22:00:53 -0700199 /* attach keyring */
David Howells1a26feb2006-04-10 22:54:26 -0700200 spin_lock_irq(&tsk->sighand->siglock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 if (!tsk->signal->process_keyring) {
202 tsk->signal->process_keyring = keyring;
203 keyring = NULL;
204 }
David Howells1a26feb2006-04-10 22:54:26 -0700205 spin_unlock_irq(&tsk->sighand->siglock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
207 key_put(keyring);
208 }
209
210 ret = 0;
David Howells664cceb2005-09-28 17:03:15 +0100211error:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 return ret;
213
214} /* end install_process_keyring() */
215
216/*****************************************************************************/
217/*
218 * install a session keyring, discarding the old one
219 * - if a keyring is not supplied, an empty one is invented
220 */
221static int install_session_keyring(struct task_struct *tsk,
222 struct key *keyring)
223{
David Howells7e047ef2006-06-26 00:24:50 -0700224 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 struct key *old;
226 char buf[20];
David Howells1a26feb2006-04-10 22:54:26 -0700227
228 might_sleep();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
230 /* create an empty session keyring */
231 if (!keyring) {
232 sprintf(buf, "_ses.%u", tsk->tgid);
233
David Howells7e047ef2006-06-26 00:24:50 -0700234 flags = KEY_ALLOC_QUOTA_OVERRUN;
235 if (tsk->signal->session_keyring)
236 flags = KEY_ALLOC_IN_QUOTA;
237
238 keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
239 flags, NULL);
David Howells1a26feb2006-04-10 22:54:26 -0700240 if (IS_ERR(keyring))
241 return PTR_ERR(keyring);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 }
243 else {
244 atomic_inc(&keyring->usage);
245 }
246
247 /* install the keyring */
David Howells1a26feb2006-04-10 22:54:26 -0700248 spin_lock_irq(&tsk->sighand->siglock);
249 old = tsk->signal->session_keyring;
David Howells8589b4e2005-06-23 22:00:53 -0700250 rcu_assign_pointer(tsk->signal->session_keyring, keyring);
David Howells1a26feb2006-04-10 22:54:26 -0700251 spin_unlock_irq(&tsk->sighand->siglock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
David Howells1a26feb2006-04-10 22:54:26 -0700253 /* we're using RCU on the pointer, but there's no point synchronising
254 * on it if it didn't previously point to anything */
255 if (old) {
256 synchronize_rcu();
257 key_put(old);
258 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
David Howells1a26feb2006-04-10 22:54:26 -0700260 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262} /* end install_session_keyring() */
263
264/*****************************************************************************/
265/*
266 * copy the keys in a thread group for fork without CLONE_THREAD
267 */
268int copy_thread_group_keys(struct task_struct *tsk)
269{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 key_check(current->thread_group->session_keyring);
271 key_check(current->thread_group->process_keyring);
272
273 /* no process keyring yet */
274 tsk->signal->process_keyring = NULL;
275
276 /* same session keyring */
David Howells8589b4e2005-06-23 22:00:53 -0700277 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 tsk->signal->session_keyring =
David Howells8589b4e2005-06-23 22:00:53 -0700279 key_get(rcu_dereference(current->signal->session_keyring));
280 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282 return 0;
283
284} /* end copy_thread_group_keys() */
285
286/*****************************************************************************/
287/*
288 * copy the keys for fork
289 */
290int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
291{
292 key_check(tsk->thread_keyring);
David Howellsb5f545c2006-01-08 01:02:47 -0800293 key_check(tsk->request_key_auth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
295 /* no thread keyring yet */
296 tsk->thread_keyring = NULL;
David Howellsb5f545c2006-01-08 01:02:47 -0800297
298 /* copy the request_key() authorisation for this thread */
299 key_get(tsk->request_key_auth);
300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 return 0;
302
303} /* end copy_keys() */
304
305/*****************************************************************************/
306/*
307 * dispose of thread group keys upon thread group destruction
308 */
309void exit_thread_group_keys(struct signal_struct *tg)
310{
311 key_put(tg->session_keyring);
312 key_put(tg->process_keyring);
313
314} /* end exit_thread_group_keys() */
315
316/*****************************************************************************/
317/*
David Howellsb5f545c2006-01-08 01:02:47 -0800318 * dispose of per-thread keys upon thread exit
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 */
320void exit_keys(struct task_struct *tsk)
321{
322 key_put(tsk->thread_keyring);
David Howellsb5f545c2006-01-08 01:02:47 -0800323 key_put(tsk->request_key_auth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324
325} /* end exit_keys() */
326
327/*****************************************************************************/
328/*
329 * deal with execve()
330 */
331int exec_keys(struct task_struct *tsk)
332{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 struct key *old;
334
335 /* newly exec'd tasks don't get a thread keyring */
336 task_lock(tsk);
337 old = tsk->thread_keyring;
338 tsk->thread_keyring = NULL;
339 task_unlock(tsk);
340
341 key_put(old);
342
343 /* discard the process keyring from a newly exec'd task */
David Howells1a26feb2006-04-10 22:54:26 -0700344 spin_lock_irq(&tsk->sighand->siglock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 old = tsk->signal->process_keyring;
346 tsk->signal->process_keyring = NULL;
David Howells1a26feb2006-04-10 22:54:26 -0700347 spin_unlock_irq(&tsk->sighand->siglock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349 key_put(old);
350
351 return 0;
352
353} /* end exec_keys() */
354
355/*****************************************************************************/
356/*
357 * deal with SUID programs
358 * - we might want to make this invent a new session keyring
359 */
360int suid_keys(struct task_struct *tsk)
361{
362 return 0;
363
364} /* end suid_keys() */
365
366/*****************************************************************************/
367/*
368 * the filesystem user ID changed
369 */
370void key_fsuid_changed(struct task_struct *tsk)
371{
372 /* update the ownership of the thread keyring */
373 if (tsk->thread_keyring) {
374 down_write(&tsk->thread_keyring->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 tsk->thread_keyring->uid = tsk->fsuid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 up_write(&tsk->thread_keyring->sem);
377 }
378
379} /* end key_fsuid_changed() */
380
381/*****************************************************************************/
382/*
383 * the filesystem group ID changed
384 */
385void key_fsgid_changed(struct task_struct *tsk)
386{
387 /* update the ownership of the thread keyring */
388 if (tsk->thread_keyring) {
389 down_write(&tsk->thread_keyring->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 tsk->thread_keyring->gid = tsk->fsgid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 up_write(&tsk->thread_keyring->sem);
392 }
393
394} /* end key_fsgid_changed() */
395
396/*****************************************************************************/
397/*
398 * search the process keyrings for the first matching key
399 * - we use the supplied match function to see if the description (or other
400 * feature of interest) matches
401 * - we return -EAGAIN if we didn't find any matching key
402 * - we return -ENOKEY if we found only negative matching keys
403 */
David Howells664cceb2005-09-28 17:03:15 +0100404key_ref_t search_process_keyrings(struct key_type *type,
405 const void *description,
406 key_match_func_t match,
407 struct task_struct *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
David Howells3e301482005-06-23 22:00:56 -0700409 struct request_key_auth *rka;
David Howellsb5f545c2006-01-08 01:02:47 -0800410 key_ref_t key_ref, ret, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
David Howells04c567d2006-06-22 14:47:18 -0700412 might_sleep();
413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
415 * searchable, but we failed to find a key or we found a negative key;
416 * otherwise we want to return a sample error (probably -EACCES) if
417 * none of the keyrings were searchable
418 *
419 * in terms of priority: success > -ENOKEY > -EAGAIN > other error
420 */
David Howells664cceb2005-09-28 17:03:15 +0100421 key_ref = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 ret = NULL;
423 err = ERR_PTR(-EAGAIN);
424
425 /* search the thread keyring first */
David Howells3e301482005-06-23 22:00:56 -0700426 if (context->thread_keyring) {
David Howells664cceb2005-09-28 17:03:15 +0100427 key_ref = keyring_search_aux(
428 make_key_ref(context->thread_keyring, 1),
429 context, type, description, match);
430 if (!IS_ERR(key_ref))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 goto found;
432
David Howells664cceb2005-09-28 17:03:15 +0100433 switch (PTR_ERR(key_ref)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 case -EAGAIN: /* no key */
435 if (ret)
436 break;
437 case -ENOKEY: /* negative key */
David Howells664cceb2005-09-28 17:03:15 +0100438 ret = key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 break;
440 default:
David Howells664cceb2005-09-28 17:03:15 +0100441 err = key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 break;
443 }
444 }
445
446 /* search the process keyring second */
David Howells3e301482005-06-23 22:00:56 -0700447 if (context->signal->process_keyring) {
David Howells664cceb2005-09-28 17:03:15 +0100448 key_ref = keyring_search_aux(
449 make_key_ref(context->signal->process_keyring, 1),
450 context, type, description, match);
451 if (!IS_ERR(key_ref))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 goto found;
453
David Howells664cceb2005-09-28 17:03:15 +0100454 switch (PTR_ERR(key_ref)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 case -EAGAIN: /* no key */
456 if (ret)
457 break;
458 case -ENOKEY: /* negative key */
David Howells664cceb2005-09-28 17:03:15 +0100459 ret = key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 break;
461 default:
David Howells664cceb2005-09-28 17:03:15 +0100462 err = key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 break;
464 }
465 }
466
David Howells3e301482005-06-23 22:00:56 -0700467 /* search the session keyring */
468 if (context->signal->session_keyring) {
David Howells8589b4e2005-06-23 22:00:53 -0700469 rcu_read_lock();
David Howells664cceb2005-09-28 17:03:15 +0100470 key_ref = keyring_search_aux(
471 make_key_ref(rcu_dereference(
472 context->signal->session_keyring),
473 1),
David Howells3e301482005-06-23 22:00:56 -0700474 context, type, description, match);
David Howells8589b4e2005-06-23 22:00:53 -0700475 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
David Howells664cceb2005-09-28 17:03:15 +0100477 if (!IS_ERR(key_ref))
David Howells3e301482005-06-23 22:00:56 -0700478 goto found;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
David Howells664cceb2005-09-28 17:03:15 +0100480 switch (PTR_ERR(key_ref)) {
David Howells3e301482005-06-23 22:00:56 -0700481 case -EAGAIN: /* no key */
482 if (ret)
483 break;
484 case -ENOKEY: /* negative key */
David Howells664cceb2005-09-28 17:03:15 +0100485 ret = key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 break;
David Howells3e301482005-06-23 22:00:56 -0700487 default:
David Howells664cceb2005-09-28 17:03:15 +0100488 err = key_ref;
David Howells3e301482005-06-23 22:00:56 -0700489 break;
490 }
David Howells3e301482005-06-23 22:00:56 -0700491 }
492 /* or search the user-session keyring */
David Howells69664cf2008-04-29 01:01:31 -0700493 else if (context->user->session_keyring) {
David Howells664cceb2005-09-28 17:03:15 +0100494 key_ref = keyring_search_aux(
495 make_key_ref(context->user->session_keyring, 1),
496 context, type, description, match);
497 if (!IS_ERR(key_ref))
David Howells3e301482005-06-23 22:00:56 -0700498 goto found;
499
David Howells664cceb2005-09-28 17:03:15 +0100500 switch (PTR_ERR(key_ref)) {
David Howells3e301482005-06-23 22:00:56 -0700501 case -EAGAIN: /* no key */
502 if (ret)
503 break;
504 case -ENOKEY: /* negative key */
David Howells664cceb2005-09-28 17:03:15 +0100505 ret = key_ref;
David Howells3e301482005-06-23 22:00:56 -0700506 break;
507 default:
David Howells664cceb2005-09-28 17:03:15 +0100508 err = key_ref;
David Howells3e301482005-06-23 22:00:56 -0700509 break;
510 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 }
512
David Howellsb5f545c2006-01-08 01:02:47 -0800513 /* if this process has an instantiation authorisation key, then we also
514 * search the keyrings of the process mentioned there
515 * - we don't permit access to request_key auth keys via this method
516 */
517 if (context->request_key_auth &&
518 context == current &&
David Howells04c567d2006-06-22 14:47:18 -0700519 type != &key_type_request_key_auth
David Howellsb5f545c2006-01-08 01:02:47 -0800520 ) {
David Howells04c567d2006-06-22 14:47:18 -0700521 /* defend against the auth key being revoked */
522 down_read(&context->request_key_auth->sem);
David Howells3e301482005-06-23 22:00:56 -0700523
David Howells04c567d2006-06-22 14:47:18 -0700524 if (key_validate(context->request_key_auth) == 0) {
525 rka = context->request_key_auth->payload.data;
David Howellsb5f545c2006-01-08 01:02:47 -0800526
David Howells04c567d2006-06-22 14:47:18 -0700527 key_ref = search_process_keyrings(type, description,
528 match, rka->context);
David Howellsb5f545c2006-01-08 01:02:47 -0800529
David Howells04c567d2006-06-22 14:47:18 -0700530 up_read(&context->request_key_auth->sem);
531
532 if (!IS_ERR(key_ref))
533 goto found;
534
535 switch (PTR_ERR(key_ref)) {
536 case -EAGAIN: /* no key */
537 if (ret)
538 break;
539 case -ENOKEY: /* negative key */
540 ret = key_ref;
David Howellsb5f545c2006-01-08 01:02:47 -0800541 break;
David Howells04c567d2006-06-22 14:47:18 -0700542 default:
543 err = key_ref;
544 break;
545 }
546 } else {
547 up_read(&context->request_key_auth->sem);
David Howellsb5f545c2006-01-08 01:02:47 -0800548 }
549 }
550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 /* no key - decide on the error we're going to go for */
David Howells664cceb2005-09-28 17:03:15 +0100552 key_ref = ret ? ret : err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
David Howells3e301482005-06-23 22:00:56 -0700554found:
David Howells664cceb2005-09-28 17:03:15 +0100555 return key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557} /* end search_process_keyrings() */
558
559/*****************************************************************************/
560/*
David Howells664cceb2005-09-28 17:03:15 +0100561 * see if the key we're looking at is the target key
562 */
563static int lookup_user_key_possessed(const struct key *key, const void *target)
564{
565 return key == target;
566
567} /* end lookup_user_key_possessed() */
568
569/*****************************************************************************/
570/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 * lookup a key given a key ID from userspace with a given permissions mask
572 * - don't create special keyrings unless so requested
573 * - partially constructed keys aren't found unless requested
574 */
David Howells664cceb2005-09-28 17:03:15 +0100575key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
576 int create, int partial, key_perm_t perm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
David Howells664cceb2005-09-28 17:03:15 +0100578 key_ref_t key_ref, skey_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 struct key *key;
580 int ret;
581
David Howells3e301482005-06-23 22:00:56 -0700582 if (!context)
583 context = current;
584
David Howells664cceb2005-09-28 17:03:15 +0100585 key_ref = ERR_PTR(-ENOKEY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
587 switch (id) {
588 case KEY_SPEC_THREAD_KEYRING:
David Howells3e301482005-06-23 22:00:56 -0700589 if (!context->thread_keyring) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 if (!create)
591 goto error;
592
David Howells3e301482005-06-23 22:00:56 -0700593 ret = install_thread_keyring(context);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 if (ret < 0) {
595 key = ERR_PTR(ret);
596 goto error;
597 }
598 }
599
David Howells3e301482005-06-23 22:00:56 -0700600 key = context->thread_keyring;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 atomic_inc(&key->usage);
David Howells664cceb2005-09-28 17:03:15 +0100602 key_ref = make_key_ref(key, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 break;
604
605 case KEY_SPEC_PROCESS_KEYRING:
David Howells3e301482005-06-23 22:00:56 -0700606 if (!context->signal->process_keyring) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 if (!create)
608 goto error;
609
David Howells3e301482005-06-23 22:00:56 -0700610 ret = install_process_keyring(context);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 if (ret < 0) {
612 key = ERR_PTR(ret);
613 goto error;
614 }
615 }
616
David Howells3e301482005-06-23 22:00:56 -0700617 key = context->signal->process_keyring;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 atomic_inc(&key->usage);
David Howells664cceb2005-09-28 17:03:15 +0100619 key_ref = make_key_ref(key, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 break;
621
622 case KEY_SPEC_SESSION_KEYRING:
David Howells3e301482005-06-23 22:00:56 -0700623 if (!context->signal->session_keyring) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 /* always install a session keyring upon access if one
625 * doesn't exist yet */
David Howells69664cf2008-04-29 01:01:31 -0700626 ret = install_user_keyrings(context);
627 if (ret < 0)
628 goto error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 ret = install_session_keyring(
David Howells664cceb2005-09-28 17:03:15 +0100630 context, context->user->session_keyring);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 if (ret < 0)
632 goto error;
633 }
634
David Howells3e301482005-06-23 22:00:56 -0700635 rcu_read_lock();
636 key = rcu_dereference(context->signal->session_keyring);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 atomic_inc(&key->usage);
David Howells3e301482005-06-23 22:00:56 -0700638 rcu_read_unlock();
David Howells664cceb2005-09-28 17:03:15 +0100639 key_ref = make_key_ref(key, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 break;
641
642 case KEY_SPEC_USER_KEYRING:
David Howells69664cf2008-04-29 01:01:31 -0700643 if (!context->user->uid_keyring) {
644 ret = install_user_keyrings(context);
645 if (ret < 0)
646 goto error;
647 }
648
David Howells3e301482005-06-23 22:00:56 -0700649 key = context->user->uid_keyring;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 atomic_inc(&key->usage);
David Howells664cceb2005-09-28 17:03:15 +0100651 key_ref = make_key_ref(key, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 break;
653
654 case KEY_SPEC_USER_SESSION_KEYRING:
David Howells69664cf2008-04-29 01:01:31 -0700655 if (!context->user->session_keyring) {
656 ret = install_user_keyrings(context);
657 if (ret < 0)
658 goto error;
659 }
660
David Howells3e301482005-06-23 22:00:56 -0700661 key = context->user->session_keyring;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 atomic_inc(&key->usage);
David Howells664cceb2005-09-28 17:03:15 +0100663 key_ref = make_key_ref(key, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 break;
665
666 case KEY_SPEC_GROUP_KEYRING:
667 /* group keyrings are not yet supported */
668 key = ERR_PTR(-EINVAL);
669 goto error;
670
David Howellsb5f545c2006-01-08 01:02:47 -0800671 case KEY_SPEC_REQKEY_AUTH_KEY:
672 key = context->request_key_auth;
673 if (!key)
674 goto error;
675
676 atomic_inc(&key->usage);
677 key_ref = make_key_ref(key, 1);
678 break;
679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 default:
David Howells664cceb2005-09-28 17:03:15 +0100681 key_ref = ERR_PTR(-EINVAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 if (id < 1)
683 goto error;
684
685 key = key_lookup(id);
David Howells664cceb2005-09-28 17:03:15 +0100686 if (IS_ERR(key)) {
David Howellse231c2e2008-02-07 00:15:26 -0800687 key_ref = ERR_CAST(key);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 goto error;
David Howells664cceb2005-09-28 17:03:15 +0100689 }
690
691 key_ref = make_key_ref(key, 0);
692
693 /* check to see if we possess the key */
694 skey_ref = search_process_keyrings(key->type, key,
695 lookup_user_key_possessed,
696 current);
697
698 if (!IS_ERR(skey_ref)) {
699 key_put(key);
700 key_ref = skey_ref;
701 }
702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 break;
704 }
705
David Howells76181c12007-10-16 23:29:46 -0700706 if (!partial) {
707 ret = wait_for_key_construction(key, true);
708 switch (ret) {
709 case -ERESTARTSYS:
710 goto invalid_key;
711 default:
712 if (perm)
713 goto invalid_key;
714 case 0:
715 break;
716 }
717 } else if (perm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 ret = key_validate(key);
719 if (ret < 0)
720 goto invalid_key;
721 }
722
723 ret = -EIO;
David Howells76d8aea2005-06-23 22:00:49 -0700724 if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 goto invalid_key;
726
David Howells3e301482005-06-23 22:00:56 -0700727 /* check the permissions */
David Howells29db9192005-10-30 15:02:44 -0800728 ret = key_task_permission(key_ref, context, perm);
729 if (ret < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 goto invalid_key;
731
David Howells664cceb2005-09-28 17:03:15 +0100732error:
733 return key_ref;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
David Howells664cceb2005-09-28 17:03:15 +0100735invalid_key:
736 key_ref_put(key_ref);
737 key_ref = ERR_PTR(ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 goto error;
739
740} /* end lookup_user_key() */
741
742/*****************************************************************************/
743/*
744 * join the named keyring as the session keyring if possible, or attempt to
745 * create a new one of that name if not
746 * - if the name is NULL, an empty anonymous keyring is installed instead
747 * - named session keyring joining is done with a semaphore held
748 */
749long join_session_keyring(const char *name)
750{
751 struct task_struct *tsk = current;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 struct key *keyring;
753 long ret;
754
755 /* if no name is provided, install an anonymous keyring */
756 if (!name) {
757 ret = install_session_keyring(tsk, NULL);
758 if (ret < 0)
759 goto error;
760
David Howells3e301482005-06-23 22:00:56 -0700761 rcu_read_lock();
762 ret = rcu_dereference(tsk->signal->session_keyring)->serial;
763 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 goto error;
765 }
766
767 /* allow the user to join or create a named keyring */
Ingo Molnarbb003072006-03-22 00:09:14 -0800768 mutex_lock(&key_session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
770 /* look for an existing keyring of this name */
David Howells69664cf2008-04-29 01:01:31 -0700771 keyring = find_keyring_by_name(name, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 if (PTR_ERR(keyring) == -ENOKEY) {
773 /* not found - try and create a new one */
David Howells7e047ef2006-06-26 00:24:50 -0700774 keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,
775 KEY_ALLOC_IN_QUOTA, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 if (IS_ERR(keyring)) {
777 ret = PTR_ERR(keyring);
David Howellsbcf945d2005-08-04 13:07:06 -0700778 goto error2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 }
780 }
781 else if (IS_ERR(keyring)) {
782 ret = PTR_ERR(keyring);
783 goto error2;
784 }
785
786 /* we've got a keyring - now to install it */
787 ret = install_session_keyring(tsk, keyring);
788 if (ret < 0)
789 goto error2;
790
791 ret = keyring->serial;
792 key_put(keyring);
793
David Howells664cceb2005-09-28 17:03:15 +0100794error2:
Ingo Molnarbb003072006-03-22 00:09:14 -0800795 mutex_unlock(&key_session_mutex);
David Howells664cceb2005-09-28 17:03:15 +0100796error:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 return ret;
798
799} /* end join_session_keyring() */