7b8fc18
From 7289bfaee2a42bdb56eecab0625907c045d080ba Mon Sep 17 00:00:00 2001
7b8fc18
From: Eric Biggers <ebiggers@google.com>
7b8fc18
Date: Wed, 27 Sep 2017 12:50:41 -0700
7b8fc18
Subject: [PATCH] KEYS: don't let add_key() update an uninstantiated key
7b8fc18
7b8fc18
Currently, add_key() will, when passed a key that already exists, call
7b8fc18
the key's ->update() method.  But this is heavily broken in the case
7b8fc18
where the key is uninstantiated because it doesn't call
7b8fc18
__key_instantiate_and_link().  Consequently, it doesn't do most of the
7b8fc18
things that are supposed to happen when the key is instantiated, such as
7b8fc18
setting KEY_FLAG_INSTANTIATED, clearing KEY_FLAG_USER_CONSTRUCT and
7b8fc18
awakening tasks waiting on it, and incrementing key->user->nikeys.
7b8fc18
7b8fc18
It also never takes key_construction_mutex, which means that
7b8fc18
->instantiate() can run concurrently with ->update() on the same key.
7b8fc18
In the case of the "user" and "logon" key types this causes a memory
7b8fc18
leak, at best.  Maybe even worse, the ->update() methods of the
7b8fc18
"encrypted" and "trusted" key types actually just dereference a NULL
7b8fc18
pointer when passed an uninstantiated key.
7b8fc18
7b8fc18
Therefore, change find_key_to_update() to return NULL if the found key
7b8fc18
is uninstantiated, so that add_key() replaces the key rather than
7b8fc18
instantiating it.  This seems to be better than fixing __key_update() to
7b8fc18
call __key_instantiate_and_link(), since given all the bugs noted above
7b8fc18
as well as that the existing behavior was undocumented and
7b8fc18
keyctl_instantiate() is supposed to be used instead, I doubt anyone was
7b8fc18
relying on the existing behavior.
7b8fc18
7b8fc18
This patch only affects *uninstantiated* keys.  For now we still allow a
7b8fc18
negatively instantiated key to be updated (thereby positively
7b8fc18
instantiating it), although that's broken too (the next patch fixes it)
7b8fc18
and I'm not sure that anyone actually uses that functionality either.
7b8fc18
7b8fc18
Here is a simple reproducer for the bug using the "encrypted" key type
7b8fc18
(requires CONFIG_ENCRYPTED_KEYS=y), though as noted above the bug
7b8fc18
pertained to more than just the "encrypted" key type:
7b8fc18
7b8fc18
    #include <stdlib.h>
7b8fc18
    #include <unistd.h>
7b8fc18
    #include <keyutils.h>
7b8fc18
7b8fc18
    int main(void)
7b8fc18
    {
7b8fc18
        int ringid = keyctl_join_session_keyring(NULL);
7b8fc18
7b8fc18
        if (fork()) {
7b8fc18
            for (;;) {
7b8fc18
                const char payload[] = "update user:foo 32";
7b8fc18
7b8fc18
                usleep(rand() % 10000);
7b8fc18
                add_key("encrypted", "desc", payload, sizeof(payload), ringid);
7b8fc18
                keyctl_clear(ringid);
7b8fc18
            }
7b8fc18
        } else {
7b8fc18
            for (;;)
7b8fc18
                request_key("encrypted", "desc", "callout_info", ringid);
7b8fc18
        }
7b8fc18
    }
7b8fc18
7b8fc18
It causes:
7b8fc18
7b8fc18
    BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
7b8fc18
    IP: encrypted_update+0xb0/0x170
7b8fc18
    PGD 7a178067 P4D 7a178067 PUD 77269067 PMD 0
7b8fc18
    PREEMPT SMP
7b8fc18
    CPU: 0 PID: 340 Comm: reproduce Tainted: G      D         4.14.0-rc1-00025-g428490e38b2e #796
7b8fc18
    Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
7b8fc18
    task: ffff8a467a39a340 task.stack: ffffb15c40770000
7b8fc18
    RIP: 0010:encrypted_update+0xb0/0x170
7b8fc18
    RSP: 0018:ffffb15c40773de8 EFLAGS: 00010246
7b8fc18
    RAX: 0000000000000000 RBX: ffff8a467a275b00 RCX: 0000000000000000
7b8fc18
    RDX: 0000000000000005 RSI: ffff8a467a275b14 RDI: ffffffffb742f303
7b8fc18
    RBP: ffffb15c40773e20 R08: 0000000000000000 R09: ffff8a467a275b17
7b8fc18
    R10: 0000000000000020 R11: 0000000000000000 R12: 0000000000000000
7b8fc18
    R13: 0000000000000000 R14: ffff8a4677057180 R15: ffff8a467a275b0f
7b8fc18
    FS:  00007f5d7fb08700(0000) GS:ffff8a467f200000(0000) knlGS:0000000000000000
7b8fc18
    CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
7b8fc18
    CR2: 0000000000000018 CR3: 0000000077262005 CR4: 00000000001606f0
7b8fc18
    Call Trace:
7b8fc18
     key_create_or_update+0x2bc/0x460
7b8fc18
     SyS_add_key+0x10c/0x1d0
7b8fc18
     entry_SYSCALL_64_fastpath+0x1f/0xbe
7b8fc18
    RIP: 0033:0x7f5d7f211259
7b8fc18
    RSP: 002b:00007ffed03904c8 EFLAGS: 00000246 ORIG_RAX: 00000000000000f8
7b8fc18
    RAX: ffffffffffffffda RBX: 000000003b2a7955 RCX: 00007f5d7f211259
7b8fc18
    RDX: 00000000004009e4 RSI: 00000000004009ff RDI: 0000000000400a04
7b8fc18
    RBP: 0000000068db8bad R08: 000000003b2a7955 R09: 0000000000000004
7b8fc18
    R10: 000000000000001a R11: 0000000000000246 R12: 0000000000400868
7b8fc18
    R13: 00007ffed03905d0 R14: 0000000000000000 R15: 0000000000000000
7b8fc18
    Code: 77 28 e8 64 34 1f 00 45 31 c0 31 c9 48 8d 55 c8 48 89 df 48 8d 75 d0 e8 ff f9 ff ff 85 c0 41 89 c4 0f 88 84 00 00 00 4c 8b 7d c8 <49> 8b 75 18 4c 89 ff e8 24 f8 ff ff 85 c0 41 89 c4 78 6d 49 8b
7b8fc18
    RIP: encrypted_update+0xb0/0x170 RSP: ffffb15c40773de8
7b8fc18
    CR2: 0000000000000018
7b8fc18
7b8fc18
Cc: <stable@vger.kernel.org>    [v2.6.12+]
7b8fc18
Signed-off-by: Eric Biggers <ebiggers@google.com>
7b8fc18
---
7b8fc18
 security/keys/keyring.c | 10 ++++++----
7b8fc18
 1 file changed, 6 insertions(+), 4 deletions(-)
7b8fc18
7b8fc18
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
7b8fc18
index 4fa82a8a9c0e..129a4175760b 100644
7b8fc18
--- a/security/keys/keyring.c
7b8fc18
+++ b/security/keys/keyring.c
7b8fc18
@@ -1056,8 +1056,8 @@ EXPORT_SYMBOL(keyring_restrict);
7b8fc18
  * caller must also hold a lock on the keyring semaphore.
7b8fc18
  *
7b8fc18
  * Returns a pointer to the found key with usage count incremented if
7b8fc18
- * successful and returns NULL if not found.  Revoked and invalidated keys are
7b8fc18
- * skipped over.
7b8fc18
+ * successful and returns NULL if not found.  Revoked, invalidated, and
7b8fc18
+ * uninstantiated keys are skipped over.  (But negative keys are not!)
7b8fc18
  *
7b8fc18
  * If successful, the possession indicator is propagated from the keyring ref
7b8fc18
  * to the returned key reference.
7b8fc18
@@ -1084,8 +1084,10 @@ key_ref_t find_key_to_update(key_ref_t keyring_ref,
7b8fc18
 
7b8fc18
 found:
7b8fc18
 	key = keyring_ptr_to_key(object);
7b8fc18
-	if (key->flags & ((1 << KEY_FLAG_INVALIDATED) |
7b8fc18
-			  (1 << KEY_FLAG_REVOKED))) {
7b8fc18
+	if ((key->flags & ((1 << KEY_FLAG_INVALIDATED) |
7b8fc18
+			   (1 << KEY_FLAG_REVOKED) |
7b8fc18
+			   (1 << KEY_FLAG_INSTANTIATED))) !=
7b8fc18
+	    (1 << KEY_FLAG_INSTANTIATED)) {
7b8fc18
 		kleave(" = NULL [x]");
7b8fc18
 		return NULL;
7b8fc18
 	}
7b8fc18
-- 
7b8fc18
2.13.6
7b8fc18