From 6fd8be2cb2d83a45fa1e2ca5126f400d015bedae Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Jan 2016 08:59:25 -0800 Subject: [PATCH] 4.3-stable patches added patches: keys-fix-keyring-ref-leak-in-join_session_keyring.patch keys-fix-race-between-read-and-revoke.patch keys-prevent-keys-from-being-removed-from-specified-keyrings.patch keys-refcount-bug-fix.patch --- ...ing-ref-leak-in-join_session_keyring.patch | 82 +++++++ ...eys-fix-race-between-read-and-revoke.patch | 115 ++++++++++ ...eing-removed-from-specified-keyrings.patch | 205 ++++++++++++++++++ queue-4.3/keys-refcount-bug-fix.patch | 84 +++++++ queue-4.3/series | 4 + 5 files changed, 490 insertions(+) create mode 100644 queue-4.3/keys-fix-keyring-ref-leak-in-join_session_keyring.patch create mode 100644 queue-4.3/keys-fix-race-between-read-and-revoke.patch create mode 100644 queue-4.3/keys-prevent-keys-from-being-removed-from-specified-keyrings.patch create mode 100644 queue-4.3/keys-refcount-bug-fix.patch diff --git a/queue-4.3/keys-fix-keyring-ref-leak-in-join_session_keyring.patch b/queue-4.3/keys-fix-keyring-ref-leak-in-join_session_keyring.patch new file mode 100644 index 00000000000..6285e4cfa18 --- /dev/null +++ b/queue-4.3/keys-fix-keyring-ref-leak-in-join_session_keyring.patch @@ -0,0 +1,82 @@ +From 23567fd052a9abb6d67fe8e7a9ccdd9800a540f2 Mon Sep 17 00:00:00 2001 +From: Yevgeny Pats +Date: Tue, 19 Jan 2016 22:09:04 +0000 +Subject: KEYS: Fix keyring ref leak in join_session_keyring() + +From: Yevgeny Pats + +commit 23567fd052a9abb6d67fe8e7a9ccdd9800a540f2 upstream. + +This fixes CVE-2016-0728. + +If a thread is asked to join as a session keyring the keyring that's already +set as its session, we leak a keyring reference. + +This can be tested with the following program: + + #include + #include + #include + #include + + int main(int argc, const char *argv[]) + { + int i = 0; + key_serial_t serial; + + serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, + "leaked-keyring"); + if (serial < 0) { + perror("keyctl"); + return -1; + } + + if (keyctl(KEYCTL_SETPERM, serial, + KEY_POS_ALL | KEY_USR_ALL) < 0) { + perror("keyctl"); + return -1; + } + + for (i = 0; i < 100; i++) { + serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, + "leaked-keyring"); + if (serial < 0) { + perror("keyctl"); + return -1; + } + } + + return 0; + } + +If, after the program has run, there something like the following line in +/proc/keys: + +3f3d898f I--Q--- 100 perm 3f3f0000 0 0 keyring leaked-keyring: empty + +with a usage count of 100 * the number of times the program has been run, +then the kernel is malfunctioning. If leaked-keyring has zero usages or +has been garbage collected, then the problem is fixed. + +Reported-by: Yevgeny Pats +Signed-off-by: David Howells +Acked-by: Don Zickus +Acked-by: Prarit Bhargava +Acked-by: Jarod Wilson +Signed-off-by: James Morris +Signed-off-by: Greg Kroah-Hartman + +--- + security/keys/process_keys.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/security/keys/process_keys.c ++++ b/security/keys/process_keys.c +@@ -794,6 +794,7 @@ long join_session_keyring(const char *na + ret = PTR_ERR(keyring); + goto error2; + } else if (keyring == new->session_keyring) { ++ key_put(keyring); + ret = 0; + goto error2; + } diff --git a/queue-4.3/keys-fix-race-between-read-and-revoke.patch b/queue-4.3/keys-fix-race-between-read-and-revoke.patch new file mode 100644 index 00000000000..c7cbb7c5832 --- /dev/null +++ b/queue-4.3/keys-fix-race-between-read-and-revoke.patch @@ -0,0 +1,115 @@ +From b4a1b4f5047e4f54e194681125c74c0aa64d637d Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Fri, 18 Dec 2015 01:34:26 +0000 +Subject: KEYS: Fix race between read and revoke + +From: David Howells + +commit b4a1b4f5047e4f54e194681125c74c0aa64d637d upstream. + +This fixes CVE-2015-7550. + +There's a race between keyctl_read() and keyctl_revoke(). If the revoke +happens between keyctl_read() checking the validity of a key and the key's +semaphore being taken, then the key type read method will see a revoked key. + +This causes a problem for the user-defined key type because it assumes in +its read method that there will always be a payload in a non-revoked key +and doesn't check for a NULL pointer. + +Fix this by making keyctl_read() check the validity of a key after taking +semaphore instead of before. + +I think the bug was introduced with the original keyrings code. + +This was discovered by a multithreaded test program generated by syzkaller +(http://github.com/google/syzkaller). Here's a cleaned up version: + + #include + #include + #include + void *thr0(void *arg) + { + key_serial_t key = (unsigned long)arg; + keyctl_revoke(key); + return 0; + } + void *thr1(void *arg) + { + key_serial_t key = (unsigned long)arg; + char buffer[16]; + keyctl_read(key, buffer, 16); + return 0; + } + int main() + { + key_serial_t key = add_key("user", "%", "foo", 3, KEY_SPEC_USER_KEYRING); + pthread_t th[5]; + pthread_create(&th[0], 0, thr0, (void *)(unsigned long)key); + pthread_create(&th[1], 0, thr1, (void *)(unsigned long)key); + pthread_create(&th[2], 0, thr0, (void *)(unsigned long)key); + pthread_create(&th[3], 0, thr1, (void *)(unsigned long)key); + pthread_join(th[0], 0); + pthread_join(th[1], 0); + pthread_join(th[2], 0); + pthread_join(th[3], 0); + return 0; + } + +Build as: + + cc -o keyctl-race keyctl-race.c -lkeyutils -lpthread + +Run as: + + while keyctl-race; do :; done + +as it may need several iterations to crash the kernel. The crash can be +summarised as: + + BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 + IP: [] user_read+0x56/0xa3 + ... + Call Trace: + [] keyctl_read_key+0xb6/0xd7 + [] SyS_keyctl+0x83/0xe0 + [] entry_SYSCALL_64_fastpath+0x12/0x6f + +Reported-by: Dmitry Vyukov +Signed-off-by: David Howells +Tested-by: Dmitry Vyukov +Signed-off-by: James Morris +Signed-off-by: Greg Kroah-Hartman + +--- + security/keys/keyctl.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +--- a/security/keys/keyctl.c ++++ b/security/keys/keyctl.c +@@ -757,16 +757,16 @@ long keyctl_read_key(key_serial_t keyid, + + /* the key is probably readable - now try to read it */ + can_read_key: +- ret = key_validate(key); +- if (ret == 0) { +- ret = -EOPNOTSUPP; +- if (key->type->read) { +- /* read the data with the semaphore held (since we +- * might sleep) */ +- down_read(&key->sem); ++ ret = -EOPNOTSUPP; ++ if (key->type->read) { ++ /* Read the data with the semaphore held (since we might sleep) ++ * to protect against the key being updated or revoked. ++ */ ++ down_read(&key->sem); ++ ret = key_validate(key); ++ if (ret == 0) + ret = key->type->read(key, buffer, buflen); +- up_read(&key->sem); +- } ++ up_read(&key->sem); + } + + error2: diff --git a/queue-4.3/keys-prevent-keys-from-being-removed-from-specified-keyrings.patch b/queue-4.3/keys-prevent-keys-from-being-removed-from-specified-keyrings.patch new file mode 100644 index 00000000000..4a08ceaa1d9 --- /dev/null +++ b/queue-4.3/keys-prevent-keys-from-being-removed-from-specified-keyrings.patch @@ -0,0 +1,205 @@ +From d3600bcf9d64d88dc1d189a754dcfab960ce751f Mon Sep 17 00:00:00 2001 +From: Mimi Zohar +Date: Tue, 10 Nov 2015 08:34:46 -0500 +Subject: KEYS: prevent keys from being removed from specified keyrings + +From: Mimi Zohar + +commit d3600bcf9d64d88dc1d189a754dcfab960ce751f upstream. + +Userspace should not be allowed to remove keys from certain keyrings +(eg. blacklist), though the keys themselves can expire. + +This patch defines a new key flag named KEY_FLAG_KEEP to prevent +userspace from being able to unlink, revoke, invalidate or timed +out a key on a keyring. When this flag is set on the keyring, all +keys subsequently added are flagged. + +In addition, when this flag is set, the keyring itself can not be +cleared. + +Signed-off-by: Mimi Zohar +Cc: David Howells +Signed-off-by: Greg Kroah-Hartman + +--- + include/linux/key.h | 1 + security/keys/key.c | 6 ++++- + security/keys/keyctl.c | 56 ++++++++++++++++++++++++++++++++++++++++--------- + 3 files changed, 52 insertions(+), 11 deletions(-) + +--- a/include/linux/key.h ++++ b/include/linux/key.h +@@ -172,6 +172,7 @@ struct key { + #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ + #define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ + #define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ ++#define KEY_FLAG_KEEP 12 /* set if key should not be removed */ + + /* the key type and key description string + * - the desc is used to match a key against search criteria +--- a/security/keys/key.c ++++ b/security/keys/key.c +@@ -429,8 +429,12 @@ static int __key_instantiate_and_link(st + awaken = 1; + + /* and link it into the destination keyring */ +- if (keyring) ++ if (keyring) { ++ if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) ++ set_bit(KEY_FLAG_KEEP, &key->flags); ++ + __key_link(key, _edit); ++ } + + /* disable the authorisation key */ + if (authkey) +--- a/security/keys/keyctl.c ++++ b/security/keys/keyctl.c +@@ -364,11 +364,14 @@ error: + * and any links to the key will be automatically garbage collected after a + * certain amount of time (/proc/sys/kernel/keys/gc_delay). + * ++ * Keys with KEY_FLAG_KEEP set should not be revoked. ++ * + * If successful, 0 is returned. + */ + long keyctl_revoke_key(key_serial_t id) + { + key_ref_t key_ref; ++ struct key *key; + long ret; + + key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); +@@ -383,8 +386,13 @@ long keyctl_revoke_key(key_serial_t id) + } + } + +- key_revoke(key_ref_to_ptr(key_ref)); +- ret = 0; ++ key = key_ref_to_ptr(key_ref); ++ if (test_bit(KEY_FLAG_KEEP, &key->flags)) ++ return -EPERM; ++ else { ++ key_revoke(key); ++ ret = 0; ++ } + + key_ref_put(key_ref); + error: +@@ -398,11 +406,14 @@ error: + * The key and any links to the key will be automatically garbage collected + * immediately. + * ++ * Keys with KEY_FLAG_KEEP set should not be invalidated. ++ * + * If successful, 0 is returned. + */ + long keyctl_invalidate_key(key_serial_t id) + { + key_ref_t key_ref; ++ struct key *key; + long ret; + + kenter("%d", id); +@@ -426,8 +437,13 @@ long keyctl_invalidate_key(key_serial_t + } + + invalidate: +- key_invalidate(key_ref_to_ptr(key_ref)); +- ret = 0; ++ key = key_ref_to_ptr(key_ref); ++ if (test_bit(KEY_FLAG_KEEP, &key->flags)) ++ ret = -EPERM; ++ else { ++ key_invalidate(key); ++ ret = 0; ++ } + error_put: + key_ref_put(key_ref); + error: +@@ -439,12 +455,13 @@ error: + * Clear the specified keyring, creating an empty process keyring if one of the + * special keyring IDs is used. + * +- * The keyring must grant the caller Write permission for this to work. If +- * successful, 0 will be returned. ++ * The keyring must grant the caller Write permission and not have ++ * KEY_FLAG_KEEP set for this to work. If successful, 0 will be returned. + */ + long keyctl_keyring_clear(key_serial_t ringid) + { + key_ref_t keyring_ref; ++ struct key *keyring; + long ret; + + keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); +@@ -466,7 +483,11 @@ long keyctl_keyring_clear(key_serial_t r + } + + clear: +- ret = keyring_clear(key_ref_to_ptr(keyring_ref)); ++ keyring = key_ref_to_ptr(keyring_ref); ++ if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) ++ ret = -EPERM; ++ else ++ ret = keyring_clear(keyring); + error_put: + key_ref_put(keyring_ref); + error: +@@ -517,11 +538,14 @@ error: + * itself need not grant the caller anything. If the last link to a key is + * removed then that key will be scheduled for destruction. + * ++ * Keys or keyrings with KEY_FLAG_KEEP set should not be unlinked. ++ * + * If successful, 0 will be returned. + */ + long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) + { + key_ref_t keyring_ref, key_ref; ++ struct key *keyring, *key; + long ret; + + keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE); +@@ -536,7 +560,13 @@ long keyctl_keyring_unlink(key_serial_t + goto error2; + } + +- ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); ++ keyring = key_ref_to_ptr(keyring_ref); ++ key = key_ref_to_ptr(key_ref); ++ if (test_bit(KEY_FLAG_KEEP, &keyring->flags) && ++ test_bit(KEY_FLAG_KEEP, &key->flags)) ++ ret = -EPERM; ++ else ++ ret = key_unlink(keyring, key); + + key_ref_put(key_ref); + error2: +@@ -1295,6 +1325,8 @@ error: + * the current time. The key and any links to the key will be automatically + * garbage collected after the timeout expires. + * ++ * Keys with KEY_FLAG_KEEP set should not be timed out. ++ * + * If successful, 0 is returned. + */ + long keyctl_set_timeout(key_serial_t id, unsigned timeout) +@@ -1326,10 +1358,14 @@ long keyctl_set_timeout(key_serial_t id, + + okay: + key = key_ref_to_ptr(key_ref); +- key_set_timeout(key, timeout); ++ if (test_bit(KEY_FLAG_KEEP, &key->flags)) ++ ret = -EPERM; ++ else { ++ key_set_timeout(key, timeout); ++ ret = 0; ++ } + key_put(key); + +- ret = 0; + error: + return ret; + } diff --git a/queue-4.3/keys-refcount-bug-fix.patch b/queue-4.3/keys-refcount-bug-fix.patch new file mode 100644 index 00000000000..c5ed3f3aac4 --- /dev/null +++ b/queue-4.3/keys-refcount-bug-fix.patch @@ -0,0 +1,84 @@ +From 1d6d167c2efcfe9539d9cffb1a1be9c92e39c2c0 Mon Sep 17 00:00:00 2001 +From: Mimi Zohar +Date: Thu, 7 Jan 2016 07:46:36 -0500 +Subject: KEYS: refcount bug fix + +From: Mimi Zohar + +commit 1d6d167c2efcfe9539d9cffb1a1be9c92e39c2c0 upstream. + +This patch fixes the key_ref leak, removes the unnecessary KEY_FLAG_KEEP +test before setting the flag, and cleans up the if/then brackets style +introduced in commit: +d3600bc KEYS: prevent keys from being removed from specified keyrings + +Reported-by: David Howells +Signed-off-by: Mimi Zohar +Acked-by: David Howells +Signed-off-by: Greg Kroah-Hartman + +--- + security/keys/key.c | 3 +-- + security/keys/keyctl.c | 17 +++++++---------- + 2 files changed, 8 insertions(+), 12 deletions(-) + +--- a/security/keys/key.c ++++ b/security/keys/key.c +@@ -430,8 +430,7 @@ static int __key_instantiate_and_link(st + + /* and link it into the destination keyring */ + if (keyring) { +- if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) +- set_bit(KEY_FLAG_KEEP, &key->flags); ++ set_bit(KEY_FLAG_KEEP, &key->flags); + + __key_link(key, _edit); + } +--- a/security/keys/keyctl.c ++++ b/security/keys/keyctl.c +@@ -387,12 +387,11 @@ long keyctl_revoke_key(key_serial_t id) + } + + key = key_ref_to_ptr(key_ref); ++ ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) +- return -EPERM; +- else { ++ ret = -EPERM; ++ else + key_revoke(key); +- ret = 0; +- } + + key_ref_put(key_ref); + error: +@@ -438,12 +437,11 @@ long keyctl_invalidate_key(key_serial_t + + invalidate: + key = key_ref_to_ptr(key_ref); ++ ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; +- else { ++ else + key_invalidate(key); +- ret = 0; +- } + error_put: + key_ref_put(key_ref); + error: +@@ -1358,12 +1356,11 @@ long keyctl_set_timeout(key_serial_t id, + + okay: + key = key_ref_to_ptr(key_ref); ++ ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; +- else { ++ else + key_set_timeout(key, timeout); +- ret = 0; +- } + key_put(key); + + error: diff --git a/queue-4.3/series b/queue-4.3/series index 8c13a592942..234b2a9e6db 100644 --- a/queue-4.3/series +++ b/queue-4.3/series @@ -51,3 +51,7 @@ fou-clean-up-socket-with-kfree_rcu.patch af_unix-revert-lock_interruptible-in-stream-receive-code.patch tcp-restore-fastopen-with-no-data-in-syn-packet.patch rhashtable-fix-walker-list-corruption.patch +keys-fix-race-between-read-and-revoke.patch +keys-prevent-keys-from-being-removed-from-specified-keyrings.patch +keys-refcount-bug-fix.patch +keys-fix-keyring-ref-leak-in-join_session_keyring.patch -- 2.47.3