KEYS: Make /proc/keys check to see if a key is possessed before security check
David Howells [Fri, 11 Jun 2010 16:31:10 +0000 (17:31 +0100)]
Make /proc/keys check to see if the calling process possesses each key before
performing the security check.  The possession check can be skipped if the key
doesn't have the possessor-view permission bit set.

This causes the keys a process possesses to show up in /proc/keys, even if they
don't have matching user/group/other view permissions.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>

security/keys/internal.h
security/keys/proc.c
security/keys/process_keys.c

index 38783dc..addb67b 100644 (file)
@@ -114,6 +114,10 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
                                    const void *description,
                                    key_match_func_t match);
 
+extern key_ref_t search_my_process_keyrings(struct key_type *type,
+                                           const void *description,
+                                           key_match_func_t match,
+                                           const struct cred *cred);
 extern key_ref_t search_process_keyrings(struct key_type *type,
                                         const void *description,
                                         key_match_func_t match,
@@ -134,6 +138,7 @@ extern struct key *request_key_and_link(struct key_type *type,
                                        struct key *dest_keyring,
                                        unsigned long flags);
 
+extern int lookup_user_key_possessed(const struct key *key, const void *target);
 extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
                                 key_perm_t perm);
 #define KEY_LOOKUP_CREATE      0x01
index 068b66e..7037396 100644 (file)
@@ -184,20 +184,36 @@ static void proc_keys_stop(struct seq_file *p, void *v)
 
 static int proc_keys_show(struct seq_file *m, void *v)
 {
+       const struct cred *cred = current_cred();
        struct rb_node *_p = v;
        struct key *key = rb_entry(_p, struct key, serial_node);
        struct timespec now;
        unsigned long timo;
+       key_ref_t key_ref, skey_ref;
        char xbuf[12];
        int rc;
 
+       key_ref = make_key_ref(key, 0);
+
+       /* determine if the key is possessed by this process (a test we can
+        * skip if the key does not indicate the possessor can view it
+        */
+       if (key->perm & KEY_POS_VIEW) {
+               skey_ref = search_my_process_keyrings(key->type, key,
+                                                     lookup_user_key_possessed,
+                                                     cred);
+               if (!IS_ERR(skey_ref)) {
+                       key_ref_put(skey_ref);
+                       key_ref = make_key_ref(key, 1);
+               }
+       }
+
        /* check whether the current task is allowed to view the key (assuming
         * non-possession)
         * - the caller holds a spinlock, and thus the RCU read lock, making our
         *   access to __current_cred() safe
         */
-       rc = key_task_permission(make_key_ref(key, 0), current_cred(),
-                                KEY_VIEW);
+       rc = key_task_permission(key_ref, cred, KEY_VIEW);
        if (rc < 0)
                return 0;
 
index 6b8e4ff..f8e7251 100644 (file)
@@ -309,22 +309,19 @@ void key_fsgid_changed(struct task_struct *tsk)
 
 /*****************************************************************************/
 /*
- * search the process keyrings for the first matching key
+ * search only my process keyrings for the first matching key
  * - we use the supplied match function to see if the description (or other
  *   feature of interest) matches
  * - we return -EAGAIN if we didn't find any matching key
  * - we return -ENOKEY if we found only negative matching keys
  */
-key_ref_t search_process_keyrings(struct key_type *type,
-                                 const void *description,
-                                 key_match_func_t match,
-                                 const struct cred *cred)
+key_ref_t search_my_process_keyrings(struct key_type *type,
+                                    const void *description,
+                                    key_match_func_t match,
+                                    const struct cred *cred)
 {
-       struct request_key_auth *rka;
        key_ref_t key_ref, ret, err;
 
-       might_sleep();
-
        /* 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;
         * otherwise we want to return a sample error (probably -EACCES) if
@@ -424,6 +421,36 @@ key_ref_t search_process_keyrings(struct key_type *type,
                }
        }
 
+       /* no key - decide on the error we're going to go for */
+       key_ref = ret ? ret : err;
+
+found:
+       return key_ref;
+}
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for the first matching key
+ * - we use the supplied match function to see if the description (or other
+ *   feature of interest) matches
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we found only negative matching keys
+ */
+key_ref_t search_process_keyrings(struct key_type *type,
+                                 const void *description,
+                                 key_match_func_t match,
+                                 const struct cred *cred)
+{
+       struct request_key_auth *rka;
+       key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
+
+       might_sleep();
+
+       key_ref = search_my_process_keyrings(type, description, match, cred);
+       if (!IS_ERR(key_ref))
+               goto found;
+       err = key_ref;
+
        /* 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
@@ -446,24 +473,19 @@ key_ref_t search_process_keyrings(struct key_type *type,
                        if (!IS_ERR(key_ref))
                                goto found;
 
-                       switch (PTR_ERR(key_ref)) {
-                       case -EAGAIN: /* no key */
-                               if (ret)
-                                       break;
-                       case -ENOKEY: /* negative key */
-                               ret = key_ref;
-                               break;
-                       default:
-                               err = key_ref;
-                               break;
-                       }
+                       ret = key_ref;
                } else {
                        up_read(&cred->request_key_auth->sem);
                }
        }
 
        /* no key - decide on the error we're going to go for */
-       key_ref = ret ? ret : err;
+       if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY))
+               key_ref = ERR_PTR(-ENOKEY);
+       else if (err == ERR_PTR(-EACCES))
+               key_ref = ret;
+       else
+               key_ref = err;
 
 found:
        return key_ref;
@@ -474,7 +496,7 @@ found:
 /*
  * see if the key we're looking at is the target key
  */
-static int lookup_user_key_possessed(const struct key *key, const void *target)
+int lookup_user_key_possessed(const struct key *key, const void *target)
 {
        return key == target;