From 7bf90ad750944df9db1fc30c84863e0c227ece15 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 22 Apr 2017 02:26:48 +0100 Subject: [PATCH] KEYS: fix keyctl_set_reqkey_keyring() to not leak thread keyrings (CVE-2017-7472) --- debian/changelog | 2 + ...set_reqkey_keyring-to-not-leak-threa.patch | 174 ++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 177 insertions(+) create mode 100644 debian/patches/bugfix/all/keys-fix-keyctl_set_reqkey_keyring-to-not-leak-threa.patch diff --git a/debian/changelog b/debian/changelog index 2ec2724d7..d95f78bfb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -327,6 +327,8 @@ linux (4.9.24-1) UNRELEASED; urgency=medium - rwsem/rt: Lift single reader restriction * KEYS: Disallow keyrings beginning with '.' to be joined as session keyrings (CVE-2016-9604) + * KEYS: fix keyctl_set_reqkey_keyring() to not leak thread keyrings + (CVE-2017-7472) [ Salvatore Bonaccorso ] * ping: implement proper locking (CVE-2017-2671) diff --git a/debian/patches/bugfix/all/keys-fix-keyctl_set_reqkey_keyring-to-not-leak-threa.patch b/debian/patches/bugfix/all/keys-fix-keyctl_set_reqkey_keyring-to-not-leak-threa.patch new file mode 100644 index 000000000..3b9eeb8fa --- /dev/null +++ b/debian/patches/bugfix/all/keys-fix-keyctl_set_reqkey_keyring-to-not-leak-threa.patch @@ -0,0 +1,174 @@ +From: Eric Biggers +Date: Tue, 18 Apr 2017 15:31:09 +0100 +Subject: KEYS: fix keyctl_set_reqkey_keyring() to not leak thread keyrings +Origin: https://git.kernel.org/linus/c9f838d104fed6f2f61d68164712e3204bf5271b +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2017-7472 + +This fixes CVE-2017-7472. + +Running the following program as an unprivileged user exhausts kernel +memory by leaking thread keyrings: + + #include + + int main() + { + for (;;) + keyctl_set_reqkey_keyring(KEY_REQKEY_DEFL_THREAD_KEYRING); + } + +Fix it by only creating a new thread keyring if there wasn't one before. +To make things more consistent, make install_thread_keyring_to_cred() +and install_process_keyring_to_cred() both return 0 if the corresponding +keyring is already present. + +Fixes: d84f4f992cbd ("CRED: Inaugurate COW credentials") +Cc: stable@vger.kernel.org # 2.6.29+ +Signed-off-by: Eric Biggers +Signed-off-by: David Howells +--- + security/keys/keyctl.c | 11 ++++------- + security/keys/process_keys.c | 44 +++++++++++++++++++++++++++----------------- + 2 files changed, 31 insertions(+), 24 deletions(-) + +--- a/security/keys/keyctl.c ++++ b/security/keys/keyctl.c +@@ -1256,8 +1256,8 @@ error: + * Read or set the default keyring in which request_key() will cache keys and + * return the old setting. + * +- * If a process keyring is specified then this will be created if it doesn't +- * yet exist. The old setting will be returned if successful. ++ * If a thread or process keyring is specified then it will be created if it ++ * doesn't yet exist. The old setting will be returned if successful. + */ + long keyctl_set_reqkey_keyring(int reqkey_defl) + { +@@ -1282,11 +1282,8 @@ long keyctl_set_reqkey_keyring(int reqke + + case KEY_REQKEY_DEFL_PROCESS_KEYRING: + ret = install_process_keyring_to_cred(new); +- if (ret < 0) { +- if (ret != -EEXIST) +- goto error; +- ret = 0; +- } ++ if (ret < 0) ++ goto error; + goto set; + + case KEY_REQKEY_DEFL_DEFAULT: +--- a/security/keys/process_keys.c ++++ b/security/keys/process_keys.c +@@ -127,13 +127,18 @@ error: + } + + /* +- * Install a fresh thread keyring directly to new credentials. This keyring is +- * allowed to overrun the quota. ++ * Install a thread keyring to the given credentials struct if it didn't have ++ * one already. This is allowed to overrun the quota. ++ * ++ * Return: 0 if a thread keyring is now present; -errno on failure. + */ + int install_thread_keyring_to_cred(struct cred *new) + { + struct key *keyring; + ++ if (new->thread_keyring) ++ return 0; ++ + keyring = keyring_alloc("_tid", new->uid, new->gid, new, + KEY_POS_ALL | KEY_USR_VIEW, + KEY_ALLOC_QUOTA_OVERRUN, +@@ -146,7 +151,9 @@ int install_thread_keyring_to_cred(struc + } + + /* +- * Install a fresh thread keyring, discarding the old one. ++ * Install a thread keyring to the current task if it didn't have one already. ++ * ++ * Return: 0 if a thread keyring is now present; -errno on failure. + */ + static int install_thread_keyring(void) + { +@@ -157,8 +164,6 @@ static int install_thread_keyring(void) + if (!new) + return -ENOMEM; + +- BUG_ON(new->thread_keyring); +- + ret = install_thread_keyring_to_cred(new); + if (ret < 0) { + abort_creds(new); +@@ -169,17 +174,17 @@ static int install_thread_keyring(void) + } + + /* +- * Install a process keyring directly to a credentials struct. ++ * Install a process keyring to the given credentials struct if it didn't have ++ * one already. This is allowed to overrun the quota. + * +- * Returns -EEXIST if there was already a process keyring, 0 if one installed, +- * and other value on any other error ++ * Return: 0 if a process keyring is now present; -errno on failure. + */ + int install_process_keyring_to_cred(struct cred *new) + { + struct key *keyring; + + if (new->process_keyring) +- return -EEXIST; ++ return 0; + + keyring = keyring_alloc("_pid", new->uid, new->gid, new, + KEY_POS_ALL | KEY_USR_VIEW, +@@ -193,11 +198,9 @@ int install_process_keyring_to_cred(stru + } + + /* +- * Make sure a process keyring is installed for the current process. The +- * existing process keyring is not replaced. ++ * Install a process keyring to the current task if it didn't have one already. + * +- * Returns 0 if there is a process keyring by the end of this function, some +- * error otherwise. ++ * Return: 0 if a process keyring is now present; -errno on failure. + */ + static int install_process_keyring(void) + { +@@ -211,14 +214,18 @@ static int install_process_keyring(void) + ret = install_process_keyring_to_cred(new); + if (ret < 0) { + abort_creds(new); +- return ret != -EEXIST ? ret : 0; ++ return ret; + } + + return commit_creds(new); + } + + /* +- * Install a session keyring directly to a credentials struct. ++ * Install the given keyring as the session keyring of the given credentials ++ * struct, replacing the existing one if any. If the given keyring is NULL, ++ * then install a new anonymous session keyring. ++ * ++ * Return: 0 on success; -errno on failure. + */ + int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) + { +@@ -253,8 +260,11 @@ int install_session_keyring_to_cred(stru + } + + /* +- * Install a session keyring, discarding the old one. If a keyring is not +- * supplied, an empty one is invented. ++ * Install the given keyring as the session keyring of the current task, ++ * replacing the existing one if any. If the given keyring is NULL, then ++ * install a new anonymous session keyring. ++ * ++ * Return: 0 on success; -errno on failure. + */ + static int install_session_keyring(struct key *keyring) + { diff --git a/debian/patches/series b/debian/patches/series index 81c50d2a8..d4df7b5c2 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -109,6 +109,7 @@ bugfix/all/net-packet-fix-overflow-in-check-for-tp_frame_nr.patch bugfix/all/net-packet-fix-overflow-in-check-for-tp_reserve.patch bugfix/all/ping-implement-proper-locking.patch bugfix/all/keys-disallow-keyrings-beginning-with-.-to-be-joined.patch +bugfix/all/keys-fix-keyctl_set_reqkey_keyring-to-not-leak-threa.patch # Fix exported symbol versions bugfix/ia64/revert-ia64-move-exports-to-definitions.patch