KEYS: Make /proc/keys check to see if a key is possessed before security check

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>
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 38783dc..addb67b 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -114,6 +114,10 @@
 				    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 @@
 					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
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 068b66e..70373966 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -184,20 +184,36 @@
 
 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;
 
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 6b8e4ff..f8e7251 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -309,22 +309,19 @@
 
 /*****************************************************************************/
 /*
- * 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 @@
 		}
 	}
 
+	/* 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 @@
 			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 @@
 /*
  * 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;