aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys/process_keys.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-06-22 17:47:18 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-22 18:05:56 -0400
commit04c567d9313e4927b9835361d8ac0318ce65af6b (patch)
treed040ef59337342603f2cc30917493fb6a74a212a /security/keys/process_keys.c
parentd720024e94de4e8b7f10ee83c532926f3ad5d708 (diff)
[PATCH] Keys: Fix race between two instantiators of a key
Add a revocation notification method to the key type and calls it whilst the key's semaphore is still write-locked after setting the revocation flag. The patch then uses this to maintain a reference on the task_struct of the process that calls request_key() for as long as the authorisation key remains unrevoked. This fixes a potential race between two processes both of which have assumed the authority to instantiate a key (one may have forked the other for example). The problem is that there's no locking around the check for revocation of the auth key and the use of the task_struct it points to, nor does the auth key keep a reference on the task_struct. Access to the "context" pointer in the auth key must thenceforth be done with the auth key semaphore held. The revocation method is called with the target key semaphore held write-locked and the search of the context process's keyrings is done with the auth key semaphore read-locked. The check for the revocation state of the auth key just prior to searching it is done after the auth key is read-locked for the search. This ensures that the auth key can't be revoked between the check and the search. The revocation notification method is added so that the context task_struct can be released as soon as instantiation happens rather than waiting for the auth key to be destroyed, thus avoiding the unnecessary pinning of the requesting process. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'security/keys/process_keys.c')
-rw-r--r--security/keys/process_keys.c42
1 files changed, 26 insertions, 16 deletions
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index a50a91332fe1..4d9825f9962c 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -391,6 +391,8 @@ key_ref_t search_process_keyrings(struct key_type *type,
391 struct request_key_auth *rka; 391 struct request_key_auth *rka;
392 key_ref_t key_ref, ret, err; 392 key_ref_t key_ref, ret, err;
393 393
394 might_sleep();
395
394 /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were 396 /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
395 * searchable, but we failed to find a key or we found a negative key; 397 * searchable, but we failed to find a key or we found a negative key;
396 * otherwise we want to return a sample error (probably -EACCES) if 398 * otherwise we want to return a sample error (probably -EACCES) if
@@ -496,27 +498,35 @@ key_ref_t search_process_keyrings(struct key_type *type,
496 */ 498 */
497 if (context->request_key_auth && 499 if (context->request_key_auth &&
498 context == current && 500 context == current &&
499 type != &key_type_request_key_auth && 501 type != &key_type_request_key_auth
500 key_validate(context->request_key_auth) == 0
501 ) { 502 ) {
502 rka = context->request_key_auth->payload.data; 503 /* defend against the auth key being revoked */
504 down_read(&context->request_key_auth->sem);
503 505
504 key_ref = search_process_keyrings(type, description, match, 506 if (key_validate(context->request_key_auth) == 0) {
505 rka->context); 507 rka = context->request_key_auth->payload.data;
506 508
507 if (!IS_ERR(key_ref)) 509 key_ref = search_process_keyrings(type, description,
508 goto found; 510 match, rka->context);
509 511
510 switch (PTR_ERR(key_ref)) { 512 up_read(&context->request_key_auth->sem);
511 case -EAGAIN: /* no key */ 513
512 if (ret) 514 if (!IS_ERR(key_ref))
515 goto found;
516
517 switch (PTR_ERR(key_ref)) {
518 case -EAGAIN: /* no key */
519 if (ret)
520 break;
521 case -ENOKEY: /* negative key */
522 ret = key_ref;
513 break; 523 break;
514 case -ENOKEY: /* negative key */ 524 default:
515 ret = key_ref; 525 err = key_ref;
516 break; 526 break;
517 default: 527 }
518 err = key_ref; 528 } else {
519 break; 529 up_read(&context->request_key_auth->sem);
520 } 530 }
521 } 531 }
522 532