Merge branch 'irq-threaded-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / security / keys / process_keys.c
index 276d278..5c23afb 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/fs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
+#include <linux/security.h>
 #include <linux/user_namespace.h>
 #include <asm/uaccess.h>
 #include "internal.h"
@@ -487,7 +488,7 @@ 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(key_serial_t id, int create, int partial,
+key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
                          key_perm_t perm)
 {
        struct request_key_auth *rka;
@@ -503,7 +504,7 @@ try_again:
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
                if (!cred->thread_keyring) {
-                       if (!create)
+                       if (!(lflags & KEY_LOOKUP_CREATE))
                                goto error;
 
                        ret = install_thread_keyring();
@@ -521,7 +522,7 @@ try_again:
 
        case KEY_SPEC_PROCESS_KEYRING:
                if (!cred->tgcred->process_keyring) {
-                       if (!create)
+                       if (!(lflags & KEY_LOOKUP_CREATE))
                                goto error;
 
                        ret = install_process_keyring();
@@ -642,7 +643,14 @@ try_again:
                break;
        }
 
-       if (!partial) {
+       /* unlink does not use the nominated key in any way, so can skip all
+        * the permission checks as it is only concerned with the keyring */
+       if (lflags & KEY_LOOKUP_FOR_UNLINK) {
+               ret = 0;
+               goto error;
+       }
+
+       if (!(lflags & KEY_LOOKUP_PARTIAL)) {
                ret = wait_for_key_construction(key, true);
                switch (ret) {
                case -ERESTARTSYS:
@@ -660,7 +668,8 @@ try_again:
        }
 
        ret = -EIO;
-       if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+       if (!(lflags & KEY_LOOKUP_PARTIAL) &&
+           !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
                goto invalid_key;
 
        /* check the permissions */
@@ -702,7 +711,7 @@ long join_session_keyring(const char *name)
        /* 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))
+       if (!current_is_single_threaded())
                return -EMLINK;
 
        new = prepare_creds();
@@ -760,3 +769,51 @@ error:
        abort_creds(new);
        return ret;
 }
+
+/*
+ * Replace a process's session keyring when that process resumes userspace on
+ * behalf of one of its children
+ */
+void key_replace_session_keyring(void)
+{
+       const struct cred *old;
+       struct cred *new;
+
+       if (!current->replacement_session_keyring)
+               return;
+
+       write_lock_irq(&tasklist_lock);
+       new = current->replacement_session_keyring;
+       current->replacement_session_keyring = NULL;
+       write_unlock_irq(&tasklist_lock);
+
+       if (!new)
+               return;
+
+       old = current_cred();
+       new->  uid      = old->  uid;
+       new-> euid      = old-> euid;
+       new-> suid      = old-> suid;
+       new->fsuid      = old->fsuid;
+       new->  gid      = old->  gid;
+       new-> egid      = old-> egid;
+       new-> sgid      = old-> sgid;
+       new->fsgid      = old->fsgid;
+       new->user       = get_uid(old->user);
+       new->group_info = get_group_info(old->group_info);
+
+       new->securebits = old->securebits;
+       new->cap_inheritable    = old->cap_inheritable;
+       new->cap_permitted      = old->cap_permitted;
+       new->cap_effective      = old->cap_effective;
+       new->cap_bset           = old->cap_bset;
+
+       new->jit_keyring        = old->jit_keyring;
+       new->thread_keyring     = key_get(old->thread_keyring);
+       new->tgcred->tgid       = old->tgcred->tgid;
+       new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
+
+       security_transfer_creds(new, old);
+
+       commit_creds(new);
+}