[PATCH] Keys: Make request-key create an authorisation key
[linux-2.6.git] / security / keys / process_keys.c
index 972e3017268705749cc6539144f0466bf344c47b..34db087bbcc7d0cdcb08266bdaf840067adb1311 100644 (file)
@@ -165,7 +165,7 @@ int install_thread_keyring(struct task_struct *tsk)
 /*
  * make sure a process keyring is installed
  */
 /*
  * make sure a process keyring is installed
  */
-static int install_process_keyring(struct task_struct *tsk)
+int install_process_keyring(struct task_struct *tsk)
 {
        unsigned long flags;
        struct key *keyring;
 {
        unsigned long flags;
        struct key *keyring;
@@ -376,12 +376,13 @@ void key_fsgid_changed(struct task_struct *tsk)
  * - we return -EAGAIN if we didn't find any matching key
  * - we return -ENOKEY if we found only negative matching keys
  */
  * - we return -EAGAIN if we didn't find any matching key
  * - we return -ENOKEY if we found only negative matching keys
  */
-struct key *search_process_keyrings_aux(struct key_type *type,
-                                       const void *description,
-                                       key_match_func_t match)
+struct key *search_process_keyrings(struct key_type *type,
+                                   const void *description,
+                                   key_match_func_t match,
+                                   struct task_struct *context)
 {
 {
-       struct task_struct *tsk = current;
-       struct key *key, *ret, *err;
+       struct request_key_auth *rka;
+       struct key *key, *ret, *err, *instkey;
 
        /* 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;
 
        /* 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;
@@ -395,9 +396,9 @@ struct key *search_process_keyrings_aux(struct key_type *type,
        err = ERR_PTR(-EAGAIN);
 
        /* search the thread keyring first */
        err = ERR_PTR(-EAGAIN);
 
        /* search the thread keyring first */
-       if (tsk->thread_keyring) {
-               key = keyring_search_aux(tsk->thread_keyring, type,
-                                        description, match);
+       if (context->thread_keyring) {
+               key = keyring_search_aux(context->thread_keyring,
+                                        context, type, description, match);
                if (!IS_ERR(key))
                        goto found;
 
                if (!IS_ERR(key))
                        goto found;
 
@@ -415,9 +416,9 @@ struct key *search_process_keyrings_aux(struct key_type *type,
        }
 
        /* search the process keyring second */
        }
 
        /* search the process keyring second */
-       if (tsk->signal->process_keyring) {
-               key = keyring_search_aux(tsk->signal->process_keyring,
-                                        type, description, match);
+       if (context->signal->process_keyring) {
+               key = keyring_search_aux(context->signal->process_keyring,
+                                        context, type, description, match);
                if (!IS_ERR(key))
                        goto found;
 
                if (!IS_ERR(key))
                        goto found;
 
@@ -434,53 +435,93 @@ struct key *search_process_keyrings_aux(struct key_type *type,
                }
        }
 
                }
        }
 
-       /* search the session keyring last */
-       if (tsk->signal->session_keyring) {
+       /* search the session keyring */
+       if (context->signal->session_keyring) {
                rcu_read_lock();
                key = keyring_search_aux(
                rcu_read_lock();
                key = keyring_search_aux(
-                       rcu_dereference(tsk->signal->session_keyring),
-                       type, description, match);
+                       rcu_dereference(context->signal->session_keyring),
+                       context, type, description, match);
                rcu_read_unlock();
                rcu_read_unlock();
+
+               if (!IS_ERR(key))
+                       goto found;
+
+               switch (PTR_ERR(key)) {
+               case -EAGAIN: /* no key */
+                       if (ret)
+                               break;
+               case -ENOKEY: /* negative key */
+                       ret = key;
+                       break;
+               default:
+                       err = key;
+                       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 = __keyring_search_one(
+                       rcu_dereference(context->signal->session_keyring),
+                       &key_type_request_key_auth, NULL, 0);
+               rcu_read_unlock();
+
+               if (IS_ERR(instkey))
+                       goto no_key;
+
+               rka = instkey->payload.data;
+
+               key = search_process_keyrings(type, description, match,
+                                             rka->context);
+               key_put(instkey);
+
+               if (!IS_ERR(key))
+                       goto found;
+
+               switch (PTR_ERR(key)) {
+               case -EAGAIN: /* no key */
+                       if (ret)
+                               break;
+               case -ENOKEY: /* negative key */
+                       ret = key;
+                       break;
+               default:
+                       err = key;
+                       break;
+               }
        }
        }
+       /* or search the user-session keyring */
        else {
        else {
-               key = keyring_search_aux(tsk->user->session_keyring,
-                                        type, description, match);
-       }
-
-       if (!IS_ERR(key))
-               goto found;
+               key = keyring_search_aux(context->user->session_keyring,
+                                        context, type, description, match);
+               if (!IS_ERR(key))
+                       goto found;
 
 
-       switch (PTR_ERR(key)) {
-       case -EAGAIN: /* no key */
-               if (ret)
+               switch (PTR_ERR(key)) {
+               case -EAGAIN: /* no key */
+                       if (ret)
+                               break;
+               case -ENOKEY: /* negative key */
+                       ret = key;
                        break;
                        break;
-       case -ENOKEY: /* negative key */
-               ret = key;
-               break;
-       default:
-               err = key;
-               break;
+               default:
+                       err = key;
+                       break;
+               }
        }
 
        }
 
+
+no_key:
        /* no key - decide on the error we're going to go for */
        key = ret ? ret : err;
 
        /* no key - decide on the error we're going to go for */
        key = ret ? ret : err;
 
- found:
+found:
        return key;
 
        return key;
 
-} /* end search_process_keyrings_aux() */
-
-/*****************************************************************************/
-/*
- * search the process keyrings for the first matching key
- * - we return -EAGAIN if we didn't find any matching key
- * - we return -ENOKEY if we found only negative matching keys
- */
-struct key *search_process_keyrings(struct key_type *type,
-                                   const char *description)
-{
-       return search_process_keyrings_aux(type, description, type->match);
-
 } /* end search_process_keyrings() */
 
 /*****************************************************************************/
 } /* end search_process_keyrings() */
 
 /*****************************************************************************/
@@ -489,72 +530,73 @@ struct key *search_process_keyrings(struct key_type *type,
  * - don't create special keyrings unless so requested
  * - partially constructed keys aren't found unless requested
  */
  * - don't create special keyrings unless so requested
  * - partially constructed keys aren't found unless requested
  */
-struct key *lookup_user_key(key_serial_t id, int create, int partial,
-                           key_perm_t perm)
+struct key *lookup_user_key(struct task_struct *context, key_serial_t id,
+                           int create, int partial, key_perm_t perm)
 {
 {
-       struct task_struct *tsk = current;
-       unsigned long flags;
        struct key *key;
        int ret;
 
        struct key *key;
        int ret;
 
+       if (!context)
+               context = current;
+
        key = ERR_PTR(-ENOKEY);
 
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
        key = ERR_PTR(-ENOKEY);
 
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
-               if (!tsk->thread_keyring) {
+               if (!context->thread_keyring) {
                        if (!create)
                                goto error;
 
                        if (!create)
                                goto error;
 
-                       ret = install_thread_keyring(tsk);
+                       ret = install_thread_keyring(context);
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
                }
 
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
                }
 
-               key = tsk->thread_keyring;
+               key = context->thread_keyring;
                atomic_inc(&key->usage);
                break;
 
        case KEY_SPEC_PROCESS_KEYRING:
                atomic_inc(&key->usage);
                break;
 
        case KEY_SPEC_PROCESS_KEYRING:
-               if (!tsk->signal->process_keyring) {
+               if (!context->signal->process_keyring) {
                        if (!create)
                                goto error;
 
                        if (!create)
                                goto error;
 
-                       ret = install_process_keyring(tsk);
+                       ret = install_process_keyring(context);
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
                }
 
                        if (ret < 0) {
                                key = ERR_PTR(ret);
                                goto error;
                        }
                }
 
-               key = tsk->signal->process_keyring;
+               key = context->signal->process_keyring;
                atomic_inc(&key->usage);
                break;
 
        case KEY_SPEC_SESSION_KEYRING:
                atomic_inc(&key->usage);
                break;
 
        case KEY_SPEC_SESSION_KEYRING:
-               if (!tsk->signal->session_keyring) {
+               if (!context->signal->session_keyring) {
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
                        ret = install_session_keyring(
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
                        ret = install_session_keyring(
-                              tsk, tsk->user->session_keyring);
+                              context, context->user->session_keyring);
                        if (ret < 0)
                                goto error;
                }
 
                        if (ret < 0)
                                goto error;
                }
 
-               spin_lock_irqsave(&tsk->sighand->siglock, flags);
-               key = tsk->signal->session_keyring;
+               rcu_read_lock();
+               key = rcu_dereference(context->signal->session_keyring);
                atomic_inc(&key->usage);
                atomic_inc(&key->usage);
-               spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+               rcu_read_unlock();
                break;
 
        case KEY_SPEC_USER_KEYRING:
                break;
 
        case KEY_SPEC_USER_KEYRING:
-               key = tsk->user->uid_keyring;
+               key = context->user->uid_keyring;
                atomic_inc(&key->usage);
                break;
 
        case KEY_SPEC_USER_SESSION_KEYRING:
                atomic_inc(&key->usage);
                break;
 
        case KEY_SPEC_USER_SESSION_KEYRING:
-               key = tsk->user->session_keyring;
+               key = context->user->session_keyring;
                atomic_inc(&key->usage);
                break;
 
                atomic_inc(&key->usage);
                break;
 
@@ -574,7 +616,7 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial,
                break;
        }
 
                break;
        }
 
-       /* check the status and permissions */
+       /* check the status */
        if (perm) {
                ret = key_validate(key);
                if (ret < 0)
        if (perm) {
                ret = key_validate(key);
                if (ret < 0)
@@ -585,8 +627,10 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial,
        if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
                goto invalid_key;
 
        if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
                goto invalid_key;
 
+       /* check the permissions */
        ret = -EACCES;
        ret = -EACCES;
-       if (!key_permission(key, perm))
+
+       if (!key_task_permission(key, context, perm))
                goto invalid_key;
 
  error:
                goto invalid_key;
 
  error:
@@ -609,7 +653,6 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial,
 long join_session_keyring(const char *name)
 {
        struct task_struct *tsk = current;
 long join_session_keyring(const char *name)
 {
        struct task_struct *tsk = current;
-       unsigned long flags;
        struct key *keyring;
        long ret;
 
        struct key *keyring;
        long ret;
 
@@ -619,9 +662,9 @@ long join_session_keyring(const char *name)
                if (ret < 0)
                        goto error;
 
                if (ret < 0)
                        goto error;
 
-               spin_lock_irqsave(&tsk->sighand->siglock, flags);
-               ret = tsk->signal->session_keyring->serial;
-               spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+               rcu_read_lock();
+               ret = rcu_dereference(tsk->signal->session_keyring)->serial;
+               rcu_read_unlock();
                goto error;
        }
 
                goto error;
        }