Merge branch 'misc' of master.kernel.org:/pub/scm/linux/kernel/git/galak/powerpc...
[pandora-kernel.git] / security / keys / process_keys.c
index d42d215..217a0be 100644 (file)
 #include <linux/keyctl.h>
 #include <linux/fs.h>
 #include <linux/err.h>
+#include <linux/mutex.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
 /* session keyring create vs join semaphore */
-static DECLARE_MUTEX(key_session_sem);
+static DEFINE_MUTEX(key_session_mutex);
 
 /* the root user's tracking struct */
 struct key_user root_key_user = {
@@ -39,7 +40,7 @@ struct key root_user_keyring = {
        .type           = &key_type_keyring,
        .user           = &root_key_user,
        .sem            = __RWSEM_INITIALIZER(root_user_keyring.sem),
-       .perm           = KEY_POS_ALL | KEY_USR_ALL,
+       .perm           = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
        .flags          = 1 << KEY_FLAG_INSTANTIATED,
        .description    = "_uid.0",
 #ifdef KEY_DEBUGGING
@@ -54,7 +55,7 @@ struct key root_session_keyring = {
        .type           = &key_type_keyring,
        .user           = &root_key_user,
        .sem            = __RWSEM_INITIALIZER(root_session_keyring.sem),
-       .perm           = KEY_POS_ALL | KEY_USR_ALL,
+       .perm           = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
        .flags          = 1 << KEY_FLAG_INSTANTIATED,
        .description    = "_uid_ses.0",
 #ifdef KEY_DEBUGGING
@@ -167,11 +168,12 @@ error:
  */
 int install_process_keyring(struct task_struct *tsk)
 {
-       unsigned long flags;
        struct key *keyring;
        char buf[20];
        int ret;
 
+       might_sleep();
+
        if (!tsk->signal->process_keyring) {
                sprintf(buf, "_pid.%u", tsk->tgid);
 
@@ -182,12 +184,12 @@ int install_process_keyring(struct task_struct *tsk)
                }
 
                /* attach keyring */
-               spin_lock_irqsave(&tsk->sighand->siglock, flags);
+               spin_lock_irq(&tsk->sighand->siglock);
                if (!tsk->signal->process_keyring) {
                        tsk->signal->process_keyring = keyring;
                        keyring = NULL;
                }
-               spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+               spin_unlock_irq(&tsk->sighand->siglock);
 
                key_put(keyring);
        }
@@ -206,38 +208,37 @@ error:
 static int install_session_keyring(struct task_struct *tsk,
                                   struct key *keyring)
 {
-       unsigned long flags;
        struct key *old;
        char buf[20];
-       int ret;
+
+       might_sleep();
 
        /* create an empty session keyring */
        if (!keyring) {
                sprintf(buf, "_ses.%u", tsk->tgid);
 
                keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
-               if (IS_ERR(keyring)) {
-                       ret = PTR_ERR(keyring);
-                       goto error;
-               }
+               if (IS_ERR(keyring))
+                       return PTR_ERR(keyring);
        }
        else {
                atomic_inc(&keyring->usage);
        }
 
        /* install the keyring */
-       spin_lock_irqsave(&tsk->sighand->siglock, flags);
-       old = rcu_dereference(tsk->signal->session_keyring);
+       spin_lock_irq(&tsk->sighand->siglock);
+       old = tsk->signal->session_keyring;
        rcu_assign_pointer(tsk->signal->session_keyring, keyring);
-       spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+       spin_unlock_irq(&tsk->sighand->siglock);
 
-       ret = 0;
+       /* we're using RCU on the pointer, but there's no point synchronising
+        * on it if it didn't previously point to anything */
+       if (old) {
+               synchronize_rcu();
+               key_put(old);
+       }
 
-       /* we're using RCU on the pointer */
-       synchronize_rcu();
-       key_put(old);
-error:
-       return ret;
+       return 0;
 
 } /* end install_session_keyring() */
 
@@ -270,9 +271,14 @@ int copy_thread_group_keys(struct task_struct *tsk)
 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() */
@@ -290,11 +296,12 @@ void exit_thread_group_keys(struct signal_struct *tg)
 
 /*****************************************************************************/
 /*
- * dispose of keys upon thread exit
+ * 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() */
 
@@ -304,7 +311,6 @@ void exit_keys(struct task_struct *tsk)
  */
 int exec_keys(struct task_struct *tsk)
 {
-       unsigned long flags;
        struct key *old;
 
        /* newly exec'd tasks don't get a thread keyring */
@@ -316,10 +322,10 @@ int exec_keys(struct task_struct *tsk)
        key_put(old);
 
        /* discard the process keyring from a newly exec'd task */
-       spin_lock_irqsave(&tsk->sighand->siglock, flags);
+       spin_lock_irq(&tsk->sighand->siglock);
        old = tsk->signal->process_keyring;
        tsk->signal->process_keyring = NULL;
-       spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+       spin_unlock_irq(&tsk->sighand->siglock);
 
        key_put(old);
 
@@ -382,7 +388,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
                                  struct task_struct *context)
 {
        struct request_key_auth *rka;
-       key_ref_t key_ref, ret, err, instkey_ref;
+       key_ref_t key_ref, ret, err;
 
        /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
         * searchable, but we failed to find a key or we found a negative key;
@@ -461,30 +467,12 @@ key_ref_t search_process_keyrings(struct key_type *type,
                        err = key_ref;
                        break;
                }
-
-               /* if this process has a session keyring and that has an
-                * instantiation authorisation key in the bottom level, then we
-                * also search the keyrings of the process mentioned there */
-               if (context != current)
-                       goto no_key;
-
-               rcu_read_lock();
-               instkey_ref = __keyring_search_one(
-                       make_key_ref(rcu_dereference(
-                                            context->signal->session_keyring),
-                                    1),
-                       &key_type_request_key_auth, NULL, 0);
-               rcu_read_unlock();
-
-               if (IS_ERR(instkey_ref))
-                       goto no_key;
-
-               rka = key_ref_to_ptr(instkey_ref)->payload.data;
-
-               key_ref = search_process_keyrings(type, description, match,
-                                                 rka->context);
-               key_ref_put(instkey_ref);
-
+       }
+       /* or search the user-session keyring */
+       else {
+               key_ref = keyring_search_aux(
+                       make_key_ref(context->user->session_keyring, 1),
+                       context, type, description, match);
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -500,11 +488,21 @@ key_ref_t search_process_keyrings(struct key_type *type,
                        break;
                }
        }
-       /* or search the user-session keyring */
-       else {
-               key_ref = keyring_search_aux(
-                       make_key_ref(context->user->session_keyring, 1),
-                       context, type, description, match);
+
+       /* if this process has an instantiation authorisation key, then we also
+        * 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 &&
+           type != &key_type_request_key_auth &&
+           key_validate(context->request_key_auth) == 0
+           ) {
+               rka = context->request_key_auth->payload.data;
+
+               key_ref = search_process_keyrings(type, description, match,
+                                                 rka->context);
+
                if (!IS_ERR(key_ref))
                        goto found;
 
@@ -521,8 +519,6 @@ key_ref_t search_process_keyrings(struct key_type *type,
                }
        }
 
-
-no_key:
        /* no key - decide on the error we're going to go for */
        key_ref = ret ? ret : err;
 
@@ -628,6 +624,15 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                key = ERR_PTR(-EINVAL);
                goto error;
 
+       case KEY_SPEC_REQKEY_AUTH_KEY:
+               key = context->request_key_auth;
+               if (!key)
+                       goto error;
+
+               atomic_inc(&key->usage);
+               key_ref = make_key_ref(key, 1);
+               break;
+
        default:
                key_ref = ERR_PTR(-EINVAL);
                if (id < 1)
@@ -666,9 +671,8 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
                goto invalid_key;
 
        /* check the permissions */
-       ret = -EACCES;
-
-       if (!key_task_permission(key_ref, context, perm))
+       ret = key_task_permission(key_ref, context, perm);
+       if (ret < 0)
                goto invalid_key;
 
 error:
@@ -707,7 +711,7 @@ long join_session_keyring(const char *name)
        }
 
        /* allow the user to join or create a named keyring */
-       down(&key_session_sem);
+       mutex_lock(&key_session_mutex);
 
        /* look for an existing keyring of this name */
        keyring = find_keyring_by_name(name, 0);
@@ -733,7 +737,7 @@ long join_session_keyring(const char *name)
        key_put(keyring);
 
 error2:
-       up(&key_session_sem);
+       mutex_unlock(&key_session_mutex);
 error:
        return ret;