X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=security%2Fkeys%2Frequest_key.c;h=f573ac189a0a6595b2184b43a9d54c3b01e0d5a0;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=fd6ba06f25035c69b2e75bc0bc1f658e3c50d341;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/security/keys/request_key.c b/security/keys/request_key.c index fd6ba06f2..f573ac189 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -1,18 +1,21 @@ /* request_key.c: request a key from userspace * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. + * + * See Documentation/keys-request-key.txt */ #include #include #include #include +#include #include "internal.h" struct key_construction { @@ -26,17 +29,37 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); /*****************************************************************************/ /* * request userspace finish the construction of a key - * - execute "/sbin/request-key " - * - if callout_info is an empty string, it'll be rendered as a "-" instead + * - execute "/sbin/request-key " */ -static int call_request_key(struct key *key, - const char *op, - const char *callout_info) +static int call_sbin_request_key(struct key *key, + struct key *authkey, + const char *op, + void *aux) { struct task_struct *tsk = current; - char *argv[10], *envp[3], uid_str[12], gid_str[12]; + key_serial_t prkey, sskey; + struct key *keyring; + char *argv[9], *envp[3], uid_str[12], gid_str[12]; char key_str[12], keyring_str[3][12]; - int i; + char desc[20]; + int ret, i; + + kenter("{%d},{%d},%s", key->serial, authkey->serial, op); + + /* allocate a new session keyring */ + sprintf(desc, "_req.%u", key->serial); + + keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current, + KEY_ALLOC_QUOTA_OVERRUN, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error_alloc; + } + + /* attach the auth key to the session keyring */ + ret = __key_link(keyring, authkey); + if (ret < 0) + goto error_link; /* record the UID and GID */ sprintf(uid_str, "%d", current->fsuid); @@ -46,16 +69,25 @@ static int call_request_key(struct key *key, sprintf(key_str, "%d", key->serial); /* we specify the process's default keyrings */ - task_lock(current); sprintf(keyring_str[0], "%d", tsk->thread_keyring ? tsk->thread_keyring->serial : 0); - sprintf(keyring_str[1], "%d", - tsk->process_keyring ? tsk->process_keyring->serial : 0); - sprintf(keyring_str[2], "%d", - (tsk->session_keyring ? - tsk->session_keyring->serial : - tsk->user->session_keyring->serial)); - task_unlock(tsk); + + prkey = 0; + if (tsk->signal->process_keyring) + prkey = tsk->signal->process_keyring->serial; + + sprintf(keyring_str[1], "%d", prkey); + + if (tsk->signal->session_keyring) { + rcu_read_lock(); + sskey = rcu_dereference(tsk->signal->session_keyring)->serial; + rcu_read_unlock(); + } + else { + sskey = tsk->user->session_keyring->serial; + } + + sprintf(keyring_str[2], "%d", sskey); /* set up a minimal environment */ i = 0; @@ -73,13 +105,19 @@ static int call_request_key(struct key *key, argv[i++] = keyring_str[0]; argv[i++] = keyring_str[1]; argv[i++] = keyring_str[2]; - argv[i++] = callout_info[0] ? (char *) callout_info : "-"; argv[i] = NULL; /* do it */ - return call_usermodehelper(argv[0], argv, envp, 1); + ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, 1); + +error_link: + key_put(keyring); + +error_alloc: + kleave(" = %d", ret); + return ret; -} /* end call_request_key() */ +} /* end call_sbin_request_key() */ /*****************************************************************************/ /* @@ -89,22 +127,26 @@ static int call_request_key(struct key *key, */ static struct key *__request_key_construction(struct key_type *type, const char *description, - const char *callout_info) + const char *callout_info, + void *aux, + unsigned long flags) { + request_key_actor_t actor; struct key_construction cons; struct timespec now; - struct key *key; - int ret, negative; + struct key *key, *authkey; + int ret, negated; + + kenter("%s,%s,%s,%lx", type->name, description, callout_info, flags); /* create a key and add it to the queue */ key = key_alloc(type, description, - current->fsuid, current->fsgid, KEY_USR_ALL, 0); + current->fsuid, current->fsgid, current, KEY_POS_ALL, + flags); if (IS_ERR(key)) goto alloc_failed; - write_lock(&key->lock); - key->flags |= KEY_FLAG_USER_CONSTRUCT; - write_unlock(&key->lock); + set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); cons.key = key; list_add_tail(&cons.link, &key->user->consq); @@ -112,53 +154,71 @@ static struct key *__request_key_construction(struct key_type *type, /* we drop the construction sem here on behalf of the caller */ up_write(&key_construction_sem); + /* allocate an authorisation key */ + authkey = request_key_auth_new(key, callout_info); + if (IS_ERR(authkey)) { + ret = PTR_ERR(authkey); + authkey = NULL; + goto alloc_authkey_failed; + } + /* make the call */ - ret = call_request_key(key, "create", callout_info); + actor = call_sbin_request_key; + if (type->request_key) + actor = type->request_key; + ret = actor(key, authkey, "create", aux); if (ret < 0) goto request_failed; /* if the key wasn't instantiated, then we want to give an error */ ret = -ENOKEY; - if (!(key->flags & KEY_FLAG_INSTANTIATED)) + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto request_failed; + key_revoke(authkey); + key_put(authkey); + down_write(&key_construction_sem); list_del(&cons.link); up_write(&key_construction_sem); /* also give an error if the key was negatively instantiated */ - check_not_negative: - if (key->flags & KEY_FLAG_NEGATIVE) { +check_not_negative: + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { key_put(key); key = ERR_PTR(-ENOKEY); } - out: +out: + kleave(" = %p", key); return key; - request_failed: +request_failed: + key_revoke(authkey); + key_put(authkey); + +alloc_authkey_failed: /* it wasn't instantiated * - remove from construction queue * - mark the key as dead */ - negative = 0; + negated = 0; down_write(&key_construction_sem); list_del(&cons.link); - write_lock(&key->lock); - key->flags &= ~KEY_FLAG_USER_CONSTRUCT; - /* check it didn't get instantiated between the check and the down */ - if (!(key->flags & KEY_FLAG_INSTANTIATED)) { - key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; - negative = 1; + if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { + set_bit(KEY_FLAG_NEGATIVE, &key->flags); + set_bit(KEY_FLAG_INSTANTIATED, &key->flags); + negated = 1; } - write_unlock(&key->lock); + clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); + up_write(&key_construction_sem); - if (!negative) + if (!negated) goto check_not_negative; /* surprisingly, the key got * instantiated */ @@ -166,8 +226,18 @@ static struct key *__request_key_construction(struct key_type *type, now = current_kernel_time(); key->expiry = now.tv_sec + key_negative_timeout; - if (current->session_keyring) - key_link(current->session_keyring, key); + if (current->signal->session_keyring) { + struct key *keyring; + + rcu_read_lock(); + keyring = rcu_dereference(current->signal->session_keyring); + atomic_inc(&keyring->usage); + rcu_read_unlock(); + + key_link(keyring, key); + key_put(keyring); + } + key_put(key); /* notify anyone who was waiting */ @@ -176,7 +246,7 @@ static struct key *__request_key_construction(struct key_type *type, key = ERR_PTR(ret); goto out; - alloc_failed: +alloc_failed: up_write(&key_construction_sem); goto out; @@ -190,14 +260,19 @@ static struct key *__request_key_construction(struct key_type *type, */ static struct key *request_key_construction(struct key_type *type, const char *description, + const char *callout_info, + void *aux, struct key_user *user, - const char *callout_info) + unsigned long flags) { struct key_construction *pcons; struct key *key, *ckey; DECLARE_WAITQUEUE(myself, current); + kenter("%s,%s,{%d},%s,%lx", + type->name, description, user->uid, callout_info, flags); + /* see if there's such a key under construction already */ down_write(&key_construction_sem); @@ -212,8 +287,10 @@ static struct key *request_key_construction(struct key_type *type, } /* see about getting userspace to construct the key */ - key = __request_key_construction(type, description, callout_info); + key = __request_key_construction(type, description, callout_info, aux, + flags); error: + kleave(" = %p", key); return key; /* someone else has the same key under construction @@ -227,8 +304,10 @@ static struct key *request_key_construction(struct key_type *type, add_wait_queue(&request_key_conswq, &myself); for (;;) { - set_current_state(TASK_UNINTERRUPTIBLE); - if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT)) + set_current_state(TASK_INTERRUPTIBLE); + if (!test_bit(KEY_FLAG_USER_CONSTRUCT, &ckey->flags)) + break; + if (signal_pending(current)) break; schedule(); } @@ -247,25 +326,100 @@ static struct key *request_key_construction(struct key_type *type, } /* end request_key_construction() */ +/*****************************************************************************/ +/* + * link a freshly minted key to an appropriate destination keyring + */ +static void request_key_link(struct key *key, struct key *dest_keyring) +{ + struct task_struct *tsk = current; + struct key *drop = NULL; + + kenter("{%d},%p", key->serial, dest_keyring); + + /* find the appropriate keyring */ + if (!dest_keyring) { + switch (tsk->jit_keyring) { + case KEY_REQKEY_DEFL_DEFAULT: + case KEY_REQKEY_DEFL_THREAD_KEYRING: + dest_keyring = tsk->thread_keyring; + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_PROCESS_KEYRING: + dest_keyring = tsk->signal->process_keyring; + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_SESSION_KEYRING: + rcu_read_lock(); + dest_keyring = key_get( + rcu_dereference(tsk->signal->session_keyring)); + rcu_read_unlock(); + drop = dest_keyring; + + if (dest_keyring) + break; + + case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: + dest_keyring = current->user->session_keyring; + break; + + case KEY_REQKEY_DEFL_USER_KEYRING: + dest_keyring = current->user->uid_keyring; + break; + + case KEY_REQKEY_DEFL_GROUP_KEYRING: + default: + BUG(); + } + } + + /* and attach the key to it */ + key_link(dest_keyring, key); + + key_put(drop); + + kleave(""); + +} /* end request_key_link() */ + /*****************************************************************************/ /* * request a key * - search the process's keyrings * - check the list of keys being created or updated - * - call out to userspace for a key if requested (supplementary info can be - * passed) + * - call out to userspace for a key if supplementary info was provided + * - cache the key in an appropriate keyring */ -struct key *request_key(struct key_type *type, - const char *description, - const char *callout_info) +struct key *request_key_and_link(struct key_type *type, + const char *description, + const char *callout_info, + void *aux, + struct key *dest_keyring, + unsigned long flags) { struct key_user *user; struct key *key; + key_ref_t key_ref; + + kenter("%s,%s,%s,%p,%p,%lx", + type->name, description, callout_info, aux, + dest_keyring, flags); /* search all the process keyrings for a key */ - key = search_process_keyrings_aux(type, description, type->match); + key_ref = search_process_keyrings(type, description, type->match, + current); + + kdebug("search 1: %p", key_ref); - if (PTR_ERR(key) == -EAGAIN) { + if (!IS_ERR(key_ref)) { + key = key_ref_to_ptr(key_ref); + } + else if (PTR_ERR(key_ref) != -EAGAIN) { + key = ERR_PTR(PTR_ERR(key_ref)); + } + else { /* the search failed, but the keyrings were searchable, so we * should consult userspace if we can */ key = ERR_PTR(-ENOKEY); @@ -274,64 +428,95 @@ struct key *request_key(struct key_type *type, /* - get hold of the user's construction queue */ user = key_user_lookup(current->fsuid); - if (IS_ERR(user)) { - key = ERR_PTR(PTR_ERR(user)); - goto error; - } + if (!user) + goto nomem; for (;;) { + if (signal_pending(current)) + goto interrupted; + /* ask userspace (returns NULL if it waited on a key * being constructed) */ key = request_key_construction(type, description, - user, callout_info); + callout_info, aux, + user, flags); if (key) break; /* someone else made the key we want, so we need to * search again as it might now be available to us */ - key = search_process_keyrings_aux(type, description, - type->match); - if (PTR_ERR(key) != -EAGAIN) + key_ref = search_process_keyrings(type, description, + type->match, + current); + + kdebug("search 2: %p", key_ref); + + if (!IS_ERR(key_ref)) { + key = key_ref_to_ptr(key_ref); + break; + } + + if (PTR_ERR(key_ref) != -EAGAIN) { + key = ERR_PTR(PTR_ERR(key_ref)); break; + } } key_user_put(user); + + /* link the new key into the appropriate keyring */ + if (!IS_ERR(key)) + request_key_link(key, dest_keyring); } - error: +error: + kleave(" = %p", key); return key; -} /* end request_key() */ +nomem: + key = ERR_PTR(-ENOMEM); + goto error; -EXPORT_SYMBOL(request_key); +interrupted: + key_user_put(user); + key = ERR_PTR(-EINTR); + goto error; + +} /* end request_key_and_link() */ /*****************************************************************************/ /* - * validate a key + * request a key + * - search the process's keyrings + * - check the list of keys being created or updated + * - call out to userspace for a key if supplementary info was provided */ -int key_validate(struct key *key) +struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info) { - struct timespec now; - int ret = 0; + return request_key_and_link(type, description, callout_info, NULL, + NULL, KEY_ALLOC_IN_QUOTA); - if (key) { - /* check it's still accessible */ - ret = -EKEYREVOKED; - if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD)) - goto error; +} /* end request_key() */ - /* check it hasn't expired */ - ret = 0; - if (key->expiry) { - now = current_kernel_time(); - if (now.tv_sec >= key->expiry) - ret = -EKEYEXPIRED; - } - } +EXPORT_SYMBOL(request_key); - error: - return ret; +/*****************************************************************************/ +/* + * request a key with auxiliary data for the upcaller + * - search the process's keyrings + * - check the list of keys being created or updated + * - call out to userspace for a key if supplementary info was provided + */ +struct key *request_key_with_auxdata(struct key_type *type, + const char *description, + const char *callout_info, + void *aux) +{ + return request_key_and_link(type, description, callout_info, aux, + NULL, KEY_ALLOC_IN_QUOTA); -} /* end key_validate() */ +} /* end request_key_with_auxdata() */ -EXPORT_SYMBOL(key_validate); +EXPORT_SYMBOL(request_key_with_auxdata);