/* request_key.c: request a key from userspace
*
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-5 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 <linux/module.h>
#include <linux/sched.h>
#include <linux/kmod.h>
#include <linux/err.h>
+#include <linux/keyctl.h>
#include "internal.h"
struct key_construction {
/*****************************************************************************/
/*
* request userspace finish the construction of a key
- * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>"
- * - if callout_info is an empty string, it'll be rendered as a "-" instead
+ * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
*/
-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)
{
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, 1, 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);
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;
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() */
/*****************************************************************************/
/*
const char *description,
const char *callout_info)
{
+ 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", type->name, description, callout_info);
/* 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, KEY_POS_ALL, 0);
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);
/* 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");
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 */
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 */
key = ERR_PTR(ret);
goto out;
- alloc_failed:
+alloc_failed:
up_write(&key_construction_sem);
goto out;
DECLARE_WAITQUEUE(myself, current);
+ kenter("%s,%s,{%d},%s",
+ type->name, description, user->uid, callout_info);
+
/* see if there's such a key under construction already */
down_write(&key_construction_sem);
/* see about getting userspace to construct the key */
key = __request_key_construction(type, description, callout_info);
error:
+ kleave(" = %p", key);
return key;
/* someone else has the same key under construction
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();
}
} /* 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,
+ struct key *dest_keyring)
{
struct key_user *user;
struct key *key;
+ key_ref_t key_ref;
+
+ kenter("%s,%s,%s,%p",
+ type->name, description, callout_info, dest_keyring);
/* 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);
- if (PTR_ERR(key) == -EAGAIN) {
+ kdebug("search 1: %p", key_ref);
+
+ 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);
/* - 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,
/* 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;
-
- if (key) {
- /* check it's still accessible */
- ret = -EKEYREVOKED;
- if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD))
- goto error;
-
- /* check it hasn't expired */
- ret = 0;
- if (key->expiry) {
- now = current_kernel_time();
- if (now.tv_sec >= key->expiry)
- ret = -EKEYEXPIRED;
- }
- }
+ return request_key_and_link(type, description, callout_info, NULL);
- error:
- return ret;
-
-} /* end key_validate() */
+} /* end request_key() */
-EXPORT_SYMBOL(key_validate);
+EXPORT_SYMBOL(request_key);