diff options
Diffstat (limited to 'security/keys/request_key.c')
-rw-r--r-- | security/keys/request_key.c | 182 |
1 files changed, 150 insertions, 32 deletions
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 54aa7b70e63b..dfcd983af1fd 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* request_key.c: request a key from userspace | 1 | /* request_key.c: request a key from userspace |
2 | * | 2 | * |
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | #include <linux/kmod.h> | 14 | #include <linux/kmod.h> |
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/keyctl.h> | ||
16 | #include "internal.h" | 17 | #include "internal.h" |
17 | 18 | ||
18 | struct key_construction { | 19 | struct key_construction { |
@@ -27,18 +28,26 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); | |||
27 | /* | 28 | /* |
28 | * request userspace finish the construction of a key | 29 | * request userspace finish the construction of a key |
29 | * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>" | 30 | * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>" |
30 | * - if callout_info is an empty string, it'll be rendered as a "-" instead | ||
31 | */ | 31 | */ |
32 | static int call_request_key(struct key *key, | 32 | static int call_request_key(struct key *key, |
33 | const char *op, | 33 | const char *op, |
34 | const char *callout_info) | 34 | const char *callout_info) |
35 | { | 35 | { |
36 | struct task_struct *tsk = current; | 36 | struct task_struct *tsk = current; |
37 | unsigned long flags; | ||
38 | key_serial_t prkey, sskey; | 37 | key_serial_t prkey, sskey; |
38 | struct key *session_keyring, *rkakey; | ||
39 | char *argv[10], *envp[3], uid_str[12], gid_str[12]; | 39 | char *argv[10], *envp[3], uid_str[12], gid_str[12]; |
40 | char key_str[12], keyring_str[3][12]; | 40 | char key_str[12], keyring_str[3][12]; |
41 | int i; | 41 | int ret, i; |
42 | |||
43 | kenter("{%d},%s,%s", key->serial, op, callout_info); | ||
44 | |||
45 | /* generate a new session keyring with an auth key in it */ | ||
46 | session_keyring = request_key_auth_new(key, &rkakey); | ||
47 | if (IS_ERR(session_keyring)) { | ||
48 | ret = PTR_ERR(session_keyring); | ||
49 | goto error; | ||
50 | } | ||
42 | 51 | ||
43 | /* record the UID and GID */ | 52 | /* record the UID and GID */ |
44 | sprintf(uid_str, "%d", current->fsuid); | 53 | sprintf(uid_str, "%d", current->fsuid); |
@@ -55,17 +64,17 @@ static int call_request_key(struct key *key, | |||
55 | if (tsk->signal->process_keyring) | 64 | if (tsk->signal->process_keyring) |
56 | prkey = tsk->signal->process_keyring->serial; | 65 | prkey = tsk->signal->process_keyring->serial; |
57 | 66 | ||
58 | sskey = 0; | 67 | sprintf(keyring_str[1], "%d", prkey); |
59 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
60 | if (tsk->signal->session_keyring) | ||
61 | sskey = tsk->signal->session_keyring->serial; | ||
62 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
63 | |||
64 | 68 | ||
65 | if (!sskey) | 69 | if (tsk->signal->session_keyring) { |
70 | rcu_read_lock(); | ||
71 | sskey = rcu_dereference(tsk->signal->session_keyring)->serial; | ||
72 | rcu_read_unlock(); | ||
73 | } | ||
74 | else { | ||
66 | sskey = tsk->user->session_keyring->serial; | 75 | sskey = tsk->user->session_keyring->serial; |
76 | } | ||
67 | 77 | ||
68 | sprintf(keyring_str[1], "%d", prkey); | ||
69 | sprintf(keyring_str[2], "%d", sskey); | 78 | sprintf(keyring_str[2], "%d", sskey); |
70 | 79 | ||
71 | /* set up a minimal environment */ | 80 | /* set up a minimal environment */ |
@@ -84,11 +93,20 @@ static int call_request_key(struct key *key, | |||
84 | argv[i++] = keyring_str[0]; | 93 | argv[i++] = keyring_str[0]; |
85 | argv[i++] = keyring_str[1]; | 94 | argv[i++] = keyring_str[1]; |
86 | argv[i++] = keyring_str[2]; | 95 | argv[i++] = keyring_str[2]; |
87 | argv[i++] = callout_info[0] ? (char *) callout_info : "-"; | 96 | argv[i++] = (char *) callout_info; |
88 | argv[i] = NULL; | 97 | argv[i] = NULL; |
89 | 98 | ||
90 | /* do it */ | 99 | /* do it */ |
91 | return call_usermodehelper_keys(argv[0], argv, envp, NULL, 1); | 100 | ret = call_usermodehelper_keys(argv[0], argv, envp, session_keyring, 1); |
101 | |||
102 | /* dispose of the special keys */ | ||
103 | key_revoke(rkakey); | ||
104 | key_put(rkakey); | ||
105 | key_put(session_keyring); | ||
106 | |||
107 | error: | ||
108 | kleave(" = %d", ret); | ||
109 | return ret; | ||
92 | 110 | ||
93 | } /* end call_request_key() */ | 111 | } /* end call_request_key() */ |
94 | 112 | ||
@@ -107,6 +125,8 @@ static struct key *__request_key_construction(struct key_type *type, | |||
107 | struct key *key; | 125 | struct key *key; |
108 | int ret, negated; | 126 | int ret, negated; |
109 | 127 | ||
128 | kenter("%s,%s,%s", type->name, description, callout_info); | ||
129 | |||
110 | /* create a key and add it to the queue */ | 130 | /* create a key and add it to the queue */ |
111 | key = key_alloc(type, description, | 131 | key = key_alloc(type, description, |
112 | current->fsuid, current->fsgid, KEY_USR_ALL, 0); | 132 | current->fsuid, current->fsgid, KEY_USR_ALL, 0); |
@@ -143,6 +163,7 @@ static struct key *__request_key_construction(struct key_type *type, | |||
143 | } | 163 | } |
144 | 164 | ||
145 | out: | 165 | out: |
166 | kleave(" = %p", key); | ||
146 | return key; | 167 | return key; |
147 | 168 | ||
148 | request_failed: | 169 | request_failed: |
@@ -216,6 +237,9 @@ static struct key *request_key_construction(struct key_type *type, | |||
216 | 237 | ||
217 | DECLARE_WAITQUEUE(myself, current); | 238 | DECLARE_WAITQUEUE(myself, current); |
218 | 239 | ||
240 | kenter("%s,%s,{%d},%s", | ||
241 | type->name, description, user->uid, callout_info); | ||
242 | |||
219 | /* see if there's such a key under construction already */ | 243 | /* see if there's such a key under construction already */ |
220 | down_write(&key_construction_sem); | 244 | down_write(&key_construction_sem); |
221 | 245 | ||
@@ -232,6 +256,7 @@ static struct key *request_key_construction(struct key_type *type, | |||
232 | /* see about getting userspace to construct the key */ | 256 | /* see about getting userspace to construct the key */ |
233 | key = __request_key_construction(type, description, callout_info); | 257 | key = __request_key_construction(type, description, callout_info); |
234 | error: | 258 | error: |
259 | kleave(" = %p", key); | ||
235 | return key; | 260 | return key; |
236 | 261 | ||
237 | /* someone else has the same key under construction | 262 | /* someone else has the same key under construction |
@@ -245,9 +270,11 @@ static struct key *request_key_construction(struct key_type *type, | |||
245 | add_wait_queue(&request_key_conswq, &myself); | 270 | add_wait_queue(&request_key_conswq, &myself); |
246 | 271 | ||
247 | for (;;) { | 272 | for (;;) { |
248 | set_current_state(TASK_UNINTERRUPTIBLE); | 273 | set_current_state(TASK_INTERRUPTIBLE); |
249 | if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags)) | 274 | if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags)) |
250 | break; | 275 | break; |
276 | if (signal_pending(current)) | ||
277 | break; | ||
251 | schedule(); | 278 | schedule(); |
252 | } | 279 | } |
253 | 280 | ||
@@ -267,21 +294,83 @@ static struct key *request_key_construction(struct key_type *type, | |||
267 | 294 | ||
268 | /*****************************************************************************/ | 295 | /*****************************************************************************/ |
269 | /* | 296 | /* |
297 | * link a freshly minted key to an appropriate destination keyring | ||
298 | */ | ||
299 | static void request_key_link(struct key *key, struct key *dest_keyring) | ||
300 | { | ||
301 | struct task_struct *tsk = current; | ||
302 | struct key *drop = NULL; | ||
303 | |||
304 | kenter("{%d},%p", key->serial, dest_keyring); | ||
305 | |||
306 | /* find the appropriate keyring */ | ||
307 | if (!dest_keyring) { | ||
308 | switch (tsk->jit_keyring) { | ||
309 | case KEY_REQKEY_DEFL_DEFAULT: | ||
310 | case KEY_REQKEY_DEFL_THREAD_KEYRING: | ||
311 | dest_keyring = tsk->thread_keyring; | ||
312 | if (dest_keyring) | ||
313 | break; | ||
314 | |||
315 | case KEY_REQKEY_DEFL_PROCESS_KEYRING: | ||
316 | dest_keyring = tsk->signal->process_keyring; | ||
317 | if (dest_keyring) | ||
318 | break; | ||
319 | |||
320 | case KEY_REQKEY_DEFL_SESSION_KEYRING: | ||
321 | rcu_read_lock(); | ||
322 | dest_keyring = key_get( | ||
323 | rcu_dereference(tsk->signal->session_keyring)); | ||
324 | rcu_read_unlock(); | ||
325 | drop = dest_keyring; | ||
326 | |||
327 | if (dest_keyring) | ||
328 | break; | ||
329 | |||
330 | case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: | ||
331 | dest_keyring = current->user->session_keyring; | ||
332 | break; | ||
333 | |||
334 | case KEY_REQKEY_DEFL_USER_KEYRING: | ||
335 | dest_keyring = current->user->uid_keyring; | ||
336 | break; | ||
337 | |||
338 | case KEY_REQKEY_DEFL_GROUP_KEYRING: | ||
339 | default: | ||
340 | BUG(); | ||
341 | } | ||
342 | } | ||
343 | |||
344 | /* and attach the key to it */ | ||
345 | key_link(dest_keyring, key); | ||
346 | |||
347 | key_put(drop); | ||
348 | |||
349 | kleave(""); | ||
350 | |||
351 | } /* end request_key_link() */ | ||
352 | |||
353 | /*****************************************************************************/ | ||
354 | /* | ||
270 | * request a key | 355 | * request a key |
271 | * - search the process's keyrings | 356 | * - search the process's keyrings |
272 | * - check the list of keys being created or updated | 357 | * - check the list of keys being created or updated |
273 | * - call out to userspace for a key if requested (supplementary info can be | 358 | * - call out to userspace for a key if supplementary info was provided |
274 | * passed) | 359 | * - cache the key in an appropriate keyring |
275 | */ | 360 | */ |
276 | struct key *request_key(struct key_type *type, | 361 | struct key *request_key_and_link(struct key_type *type, |
277 | const char *description, | 362 | const char *description, |
278 | const char *callout_info) | 363 | const char *callout_info, |
364 | struct key *dest_keyring) | ||
279 | { | 365 | { |
280 | struct key_user *user; | 366 | struct key_user *user; |
281 | struct key *key; | 367 | struct key *key; |
282 | 368 | ||
369 | kenter("%s,%s,%s,%p", | ||
370 | type->name, description, callout_info, dest_keyring); | ||
371 | |||
283 | /* search all the process keyrings for a key */ | 372 | /* search all the process keyrings for a key */ |
284 | key = search_process_keyrings_aux(type, description, type->match); | 373 | key = search_process_keyrings(type, description, type->match, current); |
285 | 374 | ||
286 | if (PTR_ERR(key) == -EAGAIN) { | 375 | if (PTR_ERR(key) == -EAGAIN) { |
287 | /* the search failed, but the keyrings were searchable, so we | 376 | /* the search failed, but the keyrings were searchable, so we |
@@ -292,12 +381,13 @@ struct key *request_key(struct key_type *type, | |||
292 | 381 | ||
293 | /* - get hold of the user's construction queue */ | 382 | /* - get hold of the user's construction queue */ |
294 | user = key_user_lookup(current->fsuid); | 383 | user = key_user_lookup(current->fsuid); |
295 | if (!user) { | 384 | if (!user) |
296 | key = ERR_PTR(-ENOMEM); | 385 | goto nomem; |
297 | goto error; | 386 | |
298 | } | 387 | do { |
388 | if (signal_pending(current)) | ||
389 | goto interrupted; | ||
299 | 390 | ||
300 | for (;;) { | ||
301 | /* ask userspace (returns NULL if it waited on a key | 391 | /* ask userspace (returns NULL if it waited on a key |
302 | * being constructed) */ | 392 | * being constructed) */ |
303 | key = request_key_construction(type, description, | 393 | key = request_key_construction(type, description, |
@@ -307,18 +397,46 @@ struct key *request_key(struct key_type *type, | |||
307 | 397 | ||
308 | /* someone else made the key we want, so we need to | 398 | /* someone else made the key we want, so we need to |
309 | * search again as it might now be available to us */ | 399 | * search again as it might now be available to us */ |
310 | key = search_process_keyrings_aux(type, description, | 400 | key = search_process_keyrings(type, description, |
311 | type->match); | 401 | type->match, current); |
312 | if (PTR_ERR(key) != -EAGAIN) | 402 | |
313 | break; | 403 | } while (PTR_ERR(key) == -EAGAIN); |
314 | } | ||
315 | 404 | ||
316 | key_user_put(user); | 405 | key_user_put(user); |
406 | |||
407 | /* link the new key into the appropriate keyring */ | ||
408 | if (!PTR_ERR(key)) | ||
409 | request_key_link(key, dest_keyring); | ||
317 | } | 410 | } |
318 | 411 | ||
319 | error: | 412 | error: |
413 | kleave(" = %p", key); | ||
320 | return key; | 414 | return key; |
321 | 415 | ||
416 | nomem: | ||
417 | key = ERR_PTR(-ENOMEM); | ||
418 | goto error; | ||
419 | |||
420 | interrupted: | ||
421 | key_user_put(user); | ||
422 | key = ERR_PTR(-EINTR); | ||
423 | goto error; | ||
424 | |||
425 | } /* end request_key_and_link() */ | ||
426 | |||
427 | /*****************************************************************************/ | ||
428 | /* | ||
429 | * request a key | ||
430 | * - search the process's keyrings | ||
431 | * - check the list of keys being created or updated | ||
432 | * - call out to userspace for a key if supplementary info was provided | ||
433 | */ | ||
434 | struct key *request_key(struct key_type *type, | ||
435 | const char *description, | ||
436 | const char *callout_info) | ||
437 | { | ||
438 | return request_key_and_link(type, description, callout_info, NULL); | ||
439 | |||
322 | } /* end request_key() */ | 440 | } /* end request_key() */ |
323 | 441 | ||
324 | EXPORT_SYMBOL(request_key); | 442 | EXPORT_SYMBOL(request_key); |