ipcns: extract create_ipc_ns()
[safe/jmp/linux-2.6] / security / keys / process_keys.c
index c886a2b..276d278 100644 (file)
@@ -1,6 +1,6 @@
-/* process_keys.c: management of a process's keyrings
+/* Management of a process's keyrings
  *
- * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
 #include <linux/fs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
+#include <linux/user_namespace.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
 /* session keyring create vs join semaphore */
 static DEFINE_MUTEX(key_session_mutex);
 
+/* user keyring creation semaphore */
+static DEFINE_MUTEX(key_user_keyring_mutex);
+
 /* the root user's tracking struct */
 struct key_user root_key_user = {
        .usage          = ATOMIC_INIT(3),
@@ -31,215 +35,214 @@ struct key_user root_key_user = {
        .nkeys          = ATOMIC_INIT(2),
        .nikeys         = ATOMIC_INIT(2),
        .uid            = 0,
-};
-
-/* the root user's UID keyring */
-struct key root_user_keyring = {
-       .usage          = ATOMIC_INIT(1),
-       .serial         = 2,
-       .type           = &key_type_keyring,
-       .user           = &root_key_user,
-       .sem            = __RWSEM_INITIALIZER(root_user_keyring.sem),
-       .perm           = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
-       .flags          = 1 << KEY_FLAG_INSTANTIATED,
-       .description    = "_uid.0",
-#ifdef KEY_DEBUGGING
-       .magic          = KEY_DEBUG_MAGIC,
-#endif
-};
-
-/* the root user's default session keyring */
-struct key root_session_keyring = {
-       .usage          = ATOMIC_INIT(1),
-       .serial         = 1,
-       .type           = &key_type_keyring,
-       .user           = &root_key_user,
-       .sem            = __RWSEM_INITIALIZER(root_session_keyring.sem),
-       .perm           = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
-       .flags          = 1 << KEY_FLAG_INSTANTIATED,
-       .description    = "_uid_ses.0",
-#ifdef KEY_DEBUGGING
-       .magic          = KEY_DEBUG_MAGIC,
-#endif
+       .user_ns        = &init_user_ns,
 };
 
 /*****************************************************************************/
 /*
- * allocate the keyrings to be associated with a UID
+ * install user and user session keyrings for a particular UID
  */
-int alloc_uid_keyring(struct user_struct *user,
-                     struct task_struct *ctx)
+int install_user_keyrings(void)
 {
+       struct user_struct *user;
+       const struct cred *cred;
        struct key *uid_keyring, *session_keyring;
        char buf[20];
        int ret;
 
-       /* concoct a default session keyring */
-       sprintf(buf, "_uid_ses.%u", user->uid);
+       cred = current_cred();
+       user = cred->user;
 
-       session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
-                                       KEY_ALLOC_IN_QUOTA, NULL);
-       if (IS_ERR(session_keyring)) {
-               ret = PTR_ERR(session_keyring);
-               goto error;
+       kenter("%p{%u}", user, user->uid);
+
+       if (user->uid_keyring) {
+               kleave(" = 0 [exist]");
+               return 0;
        }
 
-       /* and a UID specific keyring, pointed to by the default session
-        * keyring */
-       sprintf(buf, "_uid.%u", user->uid);
+       mutex_lock(&key_user_keyring_mutex);
+       ret = 0;
 
-       uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx,
-                                   KEY_ALLOC_IN_QUOTA, session_keyring);
-       if (IS_ERR(uid_keyring)) {
-               key_put(session_keyring);
-               ret = PTR_ERR(uid_keyring);
-               goto error;
+       if (!user->uid_keyring) {
+               /* get the UID-specific keyring
+                * - there may be one in existence already as it may have been
+                *   pinned by a session, but the user_struct pointing to it
+                *   may have been destroyed by setuid */
+               sprintf(buf, "_uid.%u", user->uid);
+
+               uid_keyring = find_keyring_by_name(buf, true);
+               if (IS_ERR(uid_keyring)) {
+                       uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
+                                                   cred, KEY_ALLOC_IN_QUOTA,
+                                                   NULL);
+                       if (IS_ERR(uid_keyring)) {
+                               ret = PTR_ERR(uid_keyring);
+                               goto error;
+                       }
+               }
+
+               /* get a default session keyring (which might also exist
+                * already) */
+               sprintf(buf, "_uid_ses.%u", user->uid);
+
+               session_keyring = find_keyring_by_name(buf, true);
+               if (IS_ERR(session_keyring)) {
+                       session_keyring =
+                               keyring_alloc(buf, user->uid, (gid_t) -1,
+                                             cred, KEY_ALLOC_IN_QUOTA, NULL);
+                       if (IS_ERR(session_keyring)) {
+                               ret = PTR_ERR(session_keyring);
+                               goto error_release;
+                       }
+
+                       /* we install a link from the user session keyring to
+                        * the user keyring */
+                       ret = key_link(session_keyring, uid_keyring);
+                       if (ret < 0)
+                               goto error_release_both;
+               }
+
+               /* install the keyrings */
+               user->uid_keyring = uid_keyring;
+               user->session_keyring = session_keyring;
        }
 
-       /* install the keyrings */
-       user->uid_keyring = uid_keyring;
-       user->session_keyring = session_keyring;
-       ret = 0;
+       mutex_unlock(&key_user_keyring_mutex);
+       kleave(" = 0");
+       return 0;
 
+error_release_both:
+       key_put(session_keyring);
+error_release:
+       key_put(uid_keyring);
 error:
+       mutex_unlock(&key_user_keyring_mutex);
+       kleave(" = %d", ret);
        return ret;
+}
 
-} /* end alloc_uid_keyring() */
-
-/*****************************************************************************/
 /*
- * deal with the UID changing
+ * install a fresh thread keyring directly to new credentials
  */
-void switch_uid_keyring(struct user_struct *new_user)
+int install_thread_keyring_to_cred(struct cred *new)
 {
-#if 0 /* do nothing for now */
-       struct key *old;
-
-       /* switch to the new user's session keyring if we were running under
-        * root's default session keyring */
-       if (new_user->uid != 0 &&
-           current->session_keyring == &root_session_keyring
-           ) {
-               atomic_inc(&new_user->session_keyring->usage);
-
-               task_lock(current);
-               old = current->session_keyring;
-               current->session_keyring = new_user->session_keyring;
-               task_unlock(current);
+       struct key *keyring;
 
-               key_put(old);
-       }
-#endif
+       keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       if (IS_ERR(keyring))
+               return PTR_ERR(keyring);
 
-} /* end switch_uid_keyring() */
+       new->thread_keyring = keyring;
+       return 0;
+}
 
-/*****************************************************************************/
 /*
  * install a fresh thread keyring, discarding the old one
  */
-int install_thread_keyring(struct task_struct *tsk)
+static int install_thread_keyring(void)
 {
-       struct key *keyring, *old;
-       char buf[20];
+       struct cred *new;
        int ret;
 
-       sprintf(buf, "_tid.%u", tsk->pid);
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-       keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
-                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
-       if (IS_ERR(keyring)) {
-               ret = PTR_ERR(keyring);
-               goto error;
+       BUG_ON(new->thread_keyring);
+
+       ret = install_thread_keyring_to_cred(new);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret;
        }
 
-       task_lock(tsk);
-       old = tsk->thread_keyring;
-       tsk->thread_keyring = keyring;
-       task_unlock(tsk);
+       return commit_creds(new);
+}
 
-       ret = 0;
+/*
+ * install a process keyring directly to a credentials struct
+ * - returns -EEXIST if there was already a process keyring, 0 if one installed,
+ *   and other -ve on any other error
+ */
+int install_process_keyring_to_cred(struct cred *new)
+{
+       struct key *keyring;
+       int ret;
 
-       key_put(old);
-error:
+       if (new->tgcred->process_keyring)
+               return -EEXIST;
+
+       keyring = keyring_alloc("_pid", new->uid, new->gid,
+                               new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       if (IS_ERR(keyring))
+               return PTR_ERR(keyring);
+
+       spin_lock_irq(&new->tgcred->lock);
+       if (!new->tgcred->process_keyring) {
+               new->tgcred->process_keyring = keyring;
+               keyring = NULL;
+               ret = 0;
+       } else {
+               ret = -EEXIST;
+       }
+       spin_unlock_irq(&new->tgcred->lock);
+       key_put(keyring);
        return ret;
+}
 
-} /* end install_thread_keyring() */
-
-/*****************************************************************************/
 /*
  * make sure a process keyring is installed
+ * - we
  */
-int install_process_keyring(struct task_struct *tsk)
+static int install_process_keyring(void)
 {
-       struct key *keyring;
-       char buf[20];
+       struct cred *new;
        int ret;
 
-       might_sleep();
-
-       if (!tsk->signal->process_keyring) {
-               sprintf(buf, "_pid.%u", tsk->tgid);
-
-               keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
-                                       KEY_ALLOC_QUOTA_OVERRUN, NULL);
-               if (IS_ERR(keyring)) {
-                       ret = PTR_ERR(keyring);
-                       goto error;
-               }
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-               /* attach keyring */
-               spin_lock_irq(&tsk->sighand->siglock);
-               if (!tsk->signal->process_keyring) {
-                       tsk->signal->process_keyring = keyring;
-                       keyring = NULL;
-               }
-               spin_unlock_irq(&tsk->sighand->siglock);
-
-               key_put(keyring);
+       ret = install_process_keyring_to_cred(new);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret != -EEXIST ?: 0;
        }
 
-       ret = 0;
-error:
-       return ret;
-
-} /* end install_process_keyring() */
+       return commit_creds(new);
+}
 
-/*****************************************************************************/
 /*
- * install a session keyring, discarding the old one
- * - if a keyring is not supplied, an empty one is invented
+ * install a session keyring directly to a credentials struct
  */
-static int install_session_keyring(struct task_struct *tsk,
-                                  struct key *keyring)
+static int install_session_keyring_to_cred(struct cred *cred,
+                                          struct key *keyring)
 {
        unsigned long flags;
        struct key *old;
-       char buf[20];
 
        might_sleep();
 
        /* create an empty session keyring */
        if (!keyring) {
-               sprintf(buf, "_ses.%u", tsk->tgid);
-
                flags = KEY_ALLOC_QUOTA_OVERRUN;
-               if (tsk->signal->session_keyring)
+               if (cred->tgcred->session_keyring)
                        flags = KEY_ALLOC_IN_QUOTA;
 
-               keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
-                                       flags, NULL);
+               keyring = keyring_alloc("_ses", cred->uid, cred->gid,
+                                       cred, flags, NULL);
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
-       }
-       else {
+       } else {
                atomic_inc(&keyring->usage);
        }
 
        /* install the keyring */
-       spin_lock_irq(&tsk->sighand->siglock);
-       old = tsk->signal->session_keyring;
-       rcu_assign_pointer(tsk->signal->session_keyring, keyring);
-       spin_unlock_irq(&tsk->sighand->siglock);
+       spin_lock_irq(&cred->tgcred->lock);
+       old = cred->tgcred->session_keyring;
+       rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
+       spin_unlock_irq(&cred->tgcred->lock);
 
        /* we're using RCU on the pointer, but there's no point synchronising
         * on it if it didn't previously point to anything */
@@ -249,110 +252,29 @@ static int install_session_keyring(struct task_struct *tsk,
        }
 
        return 0;
+}
 
-} /* end install_session_keyring() */
-
-/*****************************************************************************/
 /*
- * copy the keys in a thread group for fork without CLONE_THREAD
- */
-int copy_thread_group_keys(struct task_struct *tsk)
-{
-       key_check(current->thread_group->session_keyring);
-       key_check(current->thread_group->process_keyring);
-
-       /* no process keyring yet */
-       tsk->signal->process_keyring = NULL;
-
-       /* same session keyring */
-       rcu_read_lock();
-       tsk->signal->session_keyring =
-               key_get(rcu_dereference(current->signal->session_keyring));
-       rcu_read_unlock();
-
-       return 0;
-
-} /* end copy_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * copy the keys for fork
- */
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
-{
-       key_check(tsk->thread_keyring);
-       key_check(tsk->request_key_auth);
-
-       /* no thread keyring yet */
-       tsk->thread_keyring = NULL;
-
-       /* copy the request_key() authorisation for this thread */
-       key_get(tsk->request_key_auth);
-
-       return 0;
-
-} /* end copy_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of thread group keys upon thread group destruction
- */
-void exit_thread_group_keys(struct signal_struct *tg)
-{
-       key_put(tg->session_keyring);
-       key_put(tg->process_keyring);
-
-} /* end exit_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of per-thread keys upon thread exit
- */
-void exit_keys(struct task_struct *tsk)
-{
-       key_put(tsk->thread_keyring);
-       key_put(tsk->request_key_auth);
-
-} /* end exit_keys() */
-
-/*****************************************************************************/
-/*
- * deal with execve()
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
  */
-int exec_keys(struct task_struct *tsk)
+static int install_session_keyring(struct key *keyring)
 {
-       struct key *old;
-
-       /* newly exec'd tasks don't get a thread keyring */
-       task_lock(tsk);
-       old = tsk->thread_keyring;
-       tsk->thread_keyring = NULL;
-       task_unlock(tsk);
-
-       key_put(old);
-
-       /* discard the process keyring from a newly exec'd task */
-       spin_lock_irq(&tsk->sighand->siglock);
-       old = tsk->signal->process_keyring;
-       tsk->signal->process_keyring = NULL;
-       spin_unlock_irq(&tsk->sighand->siglock);
-
-       key_put(old);
-
-       return 0;
+       struct cred *new;
+       int ret;
 
-} /* end exec_keys() */
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
-/*****************************************************************************/
-/*
- * deal with SUID programs
- * - we might want to make this invent a new session keyring
- */
-int suid_keys(struct task_struct *tsk)
-{
-       return 0;
+       ret = install_session_keyring_to_cred(new, NULL);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret;
+       }
 
-} /* end suid_keys() */
+       return commit_creds(new);
+}
 
 /*****************************************************************************/
 /*
@@ -361,10 +283,11 @@ int suid_keys(struct task_struct *tsk)
 void key_fsuid_changed(struct task_struct *tsk)
 {
        /* update the ownership of the thread keyring */
-       if (tsk->thread_keyring) {
-               down_write(&tsk->thread_keyring->sem);
-               tsk->thread_keyring->uid = tsk->fsuid;
-               up_write(&tsk->thread_keyring->sem);
+       BUG_ON(!tsk->cred);
+       if (tsk->cred->thread_keyring) {
+               down_write(&tsk->cred->thread_keyring->sem);
+               tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
+               up_write(&tsk->cred->thread_keyring->sem);
        }
 
 } /* end key_fsuid_changed() */
@@ -376,10 +299,11 @@ void key_fsuid_changed(struct task_struct *tsk)
 void key_fsgid_changed(struct task_struct *tsk)
 {
        /* update the ownership of the thread keyring */
-       if (tsk->thread_keyring) {
-               down_write(&tsk->thread_keyring->sem);
-               tsk->thread_keyring->gid = tsk->fsgid;
-               up_write(&tsk->thread_keyring->sem);
+       BUG_ON(!tsk->cred);
+       if (tsk->cred->thread_keyring) {
+               down_write(&tsk->cred->thread_keyring->sem);
+               tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
+               up_write(&tsk->cred->thread_keyring->sem);
        }
 
 } /* end key_fsgid_changed() */
@@ -395,7 +319,7 @@ void key_fsgid_changed(struct task_struct *tsk)
 key_ref_t search_process_keyrings(struct key_type *type,
                                  const void *description,
                                  key_match_func_t match,
-                                 struct task_struct *context)
+                                 const struct cred *cred)
 {
        struct request_key_auth *rka;
        key_ref_t key_ref, ret, err;
@@ -414,10 +338,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
        err = ERR_PTR(-EAGAIN);
 
        /* search the thread keyring first */
-       if (context->thread_keyring) {
+       if (cred->thread_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(context->thread_keyring, 1),
-                       context, type, description, match);
+                       make_key_ref(cred->thread_keyring, 1),
+                       cred, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -435,10 +359,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
        }
 
        /* search the process keyring second */
-       if (context->signal->process_keyring) {
+       if (cred->tgcred->process_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(context->signal->process_keyring, 1),
-                       context, type, description, match);
+                       make_key_ref(cred->tgcred->process_keyring, 1),
+                       cred, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -456,13 +380,13 @@ key_ref_t search_process_keyrings(struct key_type *type,
        }
 
        /* search the session keyring */
-       if (context->signal->session_keyring) {
+       if (cred->tgcred->session_keyring) {
                rcu_read_lock();
                key_ref = keyring_search_aux(
                        make_key_ref(rcu_dereference(
-                                            context->signal->session_keyring),
+                                            cred->tgcred->session_keyring),
                                     1),
-                       context, type, description, match);
+                       cred, type, description, match);
                rcu_read_unlock();
 
                if (!IS_ERR(key_ref))
@@ -481,10 +405,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
                }
        }
        /* or search the user-session keyring */
-       else {
+       else if (cred->user->session_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(context->user->session_keyring, 1),
-                       context, type, description, match);
+                       make_key_ref(cred->user->session_keyring, 1),
+                       cred, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -505,20 +429,20 @@ key_ref_t search_process_keyrings(struct key_type *type,
         * search the keyrings of the process mentioned there
         * - we don't permit access to request_key auth keys via this method
         */
-       if (context->request_key_auth &&
-           context == current &&
+       if (cred->request_key_auth &&
+           cred == current_cred() &&
            type != &key_type_request_key_auth
            ) {
                /* defend against the auth key being revoked */
-               down_read(&context->request_key_auth->sem);
+               down_read(&cred->request_key_auth->sem);
 
-               if (key_validate(context->request_key_auth) == 0) {
-                       rka = context->request_key_auth->payload.data;
+               if (key_validate(cred->request_key_auth) == 0) {
+                       rka = cred->request_key_auth->payload.data;
 
                        key_ref = search_process_keyrings(type, description,
-                                                         match, rka->context);
+                                                         match, rka->cred);
 
-                       up_read(&context->request_key_auth->sem);
+                       up_read(&cred->request_key_auth->sem);
 
                        if (!IS_ERR(key_ref))
                                goto found;
@@ -535,7 +459,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
                                break;
                        }
                } else {
-                       up_read(&context->request_key_auth->sem);
+                       up_read(&cred->request_key_auth->sem);
                }
        }
 
@@ -563,78 +487,98 @@ static int lookup_user_key_possessed(const struct key *key, const void *target)
  * - don't create special keyrings unless so requested
  * - partially constructed keys aren't found unless requested
  */
-key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
-                         int create, int partial, key_perm_t perm)
+key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+                         key_perm_t perm)
 {
-       key_ref_t key_ref, skey_ref;
+       struct request_key_auth *rka;
+       const struct cred *cred;
        struct key *key;
+       key_ref_t key_ref, skey_ref;
        int ret;
 
-       if (!context)
-               context = current;
-
+try_again:
+       cred = get_current_cred();
        key_ref = ERR_PTR(-ENOKEY);
 
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
-               if (!context->thread_keyring) {
+               if (!cred->thread_keyring) {
                        if (!create)
                                goto error;
 
-                       ret = install_thread_keyring(context);
+                       ret = install_thread_keyring();
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
+                       goto reget_creds;
                }
 
-               key = context->thread_keyring;
+               key = cred->thread_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_PROCESS_KEYRING:
-               if (!context->signal->process_keyring) {
+               if (!cred->tgcred->process_keyring) {
                        if (!create)
                                goto error;
 
-                       ret = install_process_keyring(context);
+                       ret = install_process_keyring();
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
+                       goto reget_creds;
                }
 
-               key = context->signal->process_keyring;
+               key = cred->tgcred->process_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_SESSION_KEYRING:
-               if (!context->signal->session_keyring) {
+               if (!cred->tgcred->session_keyring) {
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
+                       ret = install_user_keyrings();
+                       if (ret < 0)
+                               goto error;
                        ret = install_session_keyring(
-                               context, context->user->session_keyring);
+                               cred->user->session_keyring);
+
                        if (ret < 0)
                                goto error;
+                       goto reget_creds;
                }
 
                rcu_read_lock();
-               key = rcu_dereference(context->signal->session_keyring);
+               key = rcu_dereference(cred->tgcred->session_keyring);
                atomic_inc(&key->usage);
                rcu_read_unlock();
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_USER_KEYRING:
-               key = context->user->uid_keyring;
+               if (!cred->user->uid_keyring) {
+                       ret = install_user_keyrings();
+                       if (ret < 0)
+                               goto error;
+               }
+
+               key = cred->user->uid_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
 
        case KEY_SPEC_USER_SESSION_KEYRING:
-               key = context->user->session_keyring;
+               if (!cred->user->session_keyring) {
+                       ret = install_user_keyrings();
+                       if (ret < 0)
+                               goto error;
+               }
+
+               key = cred->user->session_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
@@ -645,7 +589,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                goto error;
 
        case KEY_SPEC_REQKEY_AUTH_KEY:
-               key = context->request_key_auth;
+               key = cred->request_key_auth;
                if (!key)
                        goto error;
 
@@ -653,6 +597,25 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                key_ref = make_key_ref(key, 1);
                break;
 
+       case KEY_SPEC_REQUESTOR_KEYRING:
+               if (!cred->request_key_auth)
+                       goto error;
+
+               down_read(&cred->request_key_auth->sem);
+               if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
+                       key_ref = ERR_PTR(-EKEYREVOKED);
+                       key = NULL;
+               } else {
+                       rka = cred->request_key_auth->payload.data;
+                       key = rka->dest_keyring;
+                       atomic_inc(&key->usage);
+               }
+               up_read(&cred->request_key_auth->sem);
+               if (!key)
+                       goto error;
+               key_ref = make_key_ref(key, 1);
+               break;
+
        default:
                key_ref = ERR_PTR(-EINVAL);
                if (id < 1)
@@ -669,7 +632,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                /* check to see if we possess the key */
                skey_ref = search_process_keyrings(key->type, key,
                                                   lookup_user_key_possessed,
-                                                  current);
+                                                  cred);
 
                if (!IS_ERR(skey_ref)) {
                        key_put(key);
@@ -701,11 +664,12 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                goto invalid_key;
 
        /* check the permissions */
-       ret = key_task_permission(key_ref, context, perm);
+       ret = key_task_permission(key_ref, cred, perm);
        if (ret < 0)
                goto invalid_key;
 
 error:
+       put_cred(cred);
        return key_ref;
 
 invalid_key:
@@ -713,6 +677,12 @@ invalid_key:
        key_ref = ERR_PTR(ret);
        goto error;
 
+       /* if we attempted to install a keyring, then it may have caused new
+        * creds to be installed */
+reget_creds:
+       put_cred(cred);
+       goto try_again;
+
 } /* end lookup_user_key() */
 
 /*****************************************************************************/
@@ -724,52 +694,69 @@ invalid_key:
  */
 long join_session_keyring(const char *name)
 {
-       struct task_struct *tsk = current;
+       const struct cred *old;
+       struct cred *new;
        struct key *keyring;
-       long ret;
+       long ret, serial;
+
+       /* only permit this if there's a single thread in the thread group -
+        * this avoids us having to adjust the creds on all threads and risking
+        * ENOMEM */
+       if (!is_single_threaded(current))
+               return -EMLINK;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
 
        /* if no name is provided, install an anonymous keyring */
        if (!name) {
-               ret = install_session_keyring(tsk, NULL);
+               ret = install_session_keyring_to_cred(new, NULL);
                if (ret < 0)
                        goto error;
 
-               rcu_read_lock();
-               ret = rcu_dereference(tsk->signal->session_keyring)->serial;
-               rcu_read_unlock();
-               goto error;
+               serial = new->tgcred->session_keyring->serial;
+               ret = commit_creds(new);
+               if (ret == 0)
+                       ret = serial;
+               goto okay;
        }
 
        /* allow the user to join or create a named keyring */
        mutex_lock(&key_session_mutex);
 
        /* look for an existing keyring of this name */
-       keyring = find_keyring_by_name(name, 0);
+       keyring = find_keyring_by_name(name, false);
        if (PTR_ERR(keyring) == -ENOKEY) {
                /* not found - try and create a new one */
-               keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,
+               keyring = keyring_alloc(name, old->uid, old->gid, old,
                                        KEY_ALLOC_IN_QUOTA, NULL);
                if (IS_ERR(keyring)) {
                        ret = PTR_ERR(keyring);
                        goto error2;
                }
-       }
-       else if (IS_ERR(keyring)) {
+       } else if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto error2;
        }
 
        /* we've got a keyring - now to install it */
-       ret = install_session_keyring(tsk, keyring);
+       ret = install_session_keyring_to_cred(new, keyring);
        if (ret < 0)
                goto error2;
 
+       commit_creds(new);
+       mutex_unlock(&key_session_mutex);
+
        ret = keyring->serial;
        key_put(keyring);
+okay:
+       return ret;
 
 error2:
        mutex_unlock(&key_session_mutex);
 error:
+       abort_creds(new);
        return ret;
-
-} /* end join_session_keyring() */
+}