diff options
author | Eric Biggers <ebiggers@google.com> | 2017-04-18 10:31:09 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-27 03:10:37 -0400 |
commit | 174a74dbca2ddc7269c265598399c000e5b9b870 (patch) | |
tree | 44e4e9f9bf0fbf1ffd5f8fadf5211d614d9655fb | |
parent | b2dd90e812f3f733b55f0bf4487032e53b487665 (diff) |
KEYS: fix keyctl_set_reqkey_keyring() to not leak thread keyrings
commit c9f838d104fed6f2f61d68164712e3204bf5271b upstream.
This fixes CVE-2017-7472.
Running the following program as an unprivileged user exhausts kernel
memory by leaking thread keyrings:
#include <keyutils.h>
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")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | security/keys/keyctl.c | 11 | ||||
-rw-r--r-- | security/keys/process_keys.c | 44 |
2 files changed, 31 insertions, 24 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 7cdd5b550693..dbbfd7735ce5 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -1256,8 +1256,8 @@ error: | |||
1256 | * Read or set the default keyring in which request_key() will cache keys and | 1256 | * Read or set the default keyring in which request_key() will cache keys and |
1257 | * return the old setting. | 1257 | * return the old setting. |
1258 | * | 1258 | * |
1259 | * If a process keyring is specified then this will be created if it doesn't | 1259 | * If a thread or process keyring is specified then it will be created if it |
1260 | * yet exist. The old setting will be returned if successful. | 1260 | * doesn't yet exist. The old setting will be returned if successful. |
1261 | */ | 1261 | */ |
1262 | long keyctl_set_reqkey_keyring(int reqkey_defl) | 1262 | long keyctl_set_reqkey_keyring(int reqkey_defl) |
1263 | { | 1263 | { |
@@ -1282,11 +1282,8 @@ long keyctl_set_reqkey_keyring(int reqkey_defl) | |||
1282 | 1282 | ||
1283 | case KEY_REQKEY_DEFL_PROCESS_KEYRING: | 1283 | case KEY_REQKEY_DEFL_PROCESS_KEYRING: |
1284 | ret = install_process_keyring_to_cred(new); | 1284 | ret = install_process_keyring_to_cred(new); |
1285 | if (ret < 0) { | 1285 | if (ret < 0) |
1286 | if (ret != -EEXIST) | 1286 | goto error; |
1287 | goto error; | ||
1288 | ret = 0; | ||
1289 | } | ||
1290 | goto set; | 1287 | goto set; |
1291 | 1288 | ||
1292 | case KEY_REQKEY_DEFL_DEFAULT: | 1289 | case KEY_REQKEY_DEFL_DEFAULT: |
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 40a885239782..45536c677b05 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
@@ -127,13 +127,18 @@ error: | |||
127 | } | 127 | } |
128 | 128 | ||
129 | /* | 129 | /* |
130 | * Install a fresh thread keyring directly to new credentials. This keyring is | 130 | * Install a thread keyring to the given credentials struct if it didn't have |
131 | * allowed to overrun the quota. | 131 | * one already. This is allowed to overrun the quota. |
132 | * | ||
133 | * Return: 0 if a thread keyring is now present; -errno on failure. | ||
132 | */ | 134 | */ |
133 | int install_thread_keyring_to_cred(struct cred *new) | 135 | int install_thread_keyring_to_cred(struct cred *new) |
134 | { | 136 | { |
135 | struct key *keyring; | 137 | struct key *keyring; |
136 | 138 | ||
139 | if (new->thread_keyring) | ||
140 | return 0; | ||
141 | |||
137 | keyring = keyring_alloc("_tid", new->uid, new->gid, new, | 142 | keyring = keyring_alloc("_tid", new->uid, new->gid, new, |
138 | KEY_POS_ALL | KEY_USR_VIEW, | 143 | KEY_POS_ALL | KEY_USR_VIEW, |
139 | KEY_ALLOC_QUOTA_OVERRUN, | 144 | KEY_ALLOC_QUOTA_OVERRUN, |
@@ -146,7 +151,9 @@ int install_thread_keyring_to_cred(struct cred *new) | |||
146 | } | 151 | } |
147 | 152 | ||
148 | /* | 153 | /* |
149 | * Install a fresh thread keyring, discarding the old one. | 154 | * Install a thread keyring to the current task if it didn't have one already. |
155 | * | ||
156 | * Return: 0 if a thread keyring is now present; -errno on failure. | ||
150 | */ | 157 | */ |
151 | static int install_thread_keyring(void) | 158 | static int install_thread_keyring(void) |
152 | { | 159 | { |
@@ -157,8 +164,6 @@ static int install_thread_keyring(void) | |||
157 | if (!new) | 164 | if (!new) |
158 | return -ENOMEM; | 165 | return -ENOMEM; |
159 | 166 | ||
160 | BUG_ON(new->thread_keyring); | ||
161 | |||
162 | ret = install_thread_keyring_to_cred(new); | 167 | ret = install_thread_keyring_to_cred(new); |
163 | if (ret < 0) { | 168 | if (ret < 0) { |
164 | abort_creds(new); | 169 | abort_creds(new); |
@@ -169,17 +174,17 @@ static int install_thread_keyring(void) | |||
169 | } | 174 | } |
170 | 175 | ||
171 | /* | 176 | /* |
172 | * Install a process keyring directly to a credentials struct. | 177 | * Install a process keyring to the given credentials struct if it didn't have |
178 | * one already. This is allowed to overrun the quota. | ||
173 | * | 179 | * |
174 | * Returns -EEXIST if there was already a process keyring, 0 if one installed, | 180 | * Return: 0 if a process keyring is now present; -errno on failure. |
175 | * and other value on any other error | ||
176 | */ | 181 | */ |
177 | int install_process_keyring_to_cred(struct cred *new) | 182 | int install_process_keyring_to_cred(struct cred *new) |
178 | { | 183 | { |
179 | struct key *keyring; | 184 | struct key *keyring; |
180 | 185 | ||
181 | if (new->process_keyring) | 186 | if (new->process_keyring) |
182 | return -EEXIST; | 187 | return 0; |
183 | 188 | ||
184 | keyring = keyring_alloc("_pid", new->uid, new->gid, new, | 189 | keyring = keyring_alloc("_pid", new->uid, new->gid, new, |
185 | KEY_POS_ALL | KEY_USR_VIEW, | 190 | KEY_POS_ALL | KEY_USR_VIEW, |
@@ -193,11 +198,9 @@ int install_process_keyring_to_cred(struct cred *new) | |||
193 | } | 198 | } |
194 | 199 | ||
195 | /* | 200 | /* |
196 | * Make sure a process keyring is installed for the current process. The | 201 | * Install a process keyring to the current task if it didn't have one already. |
197 | * existing process keyring is not replaced. | ||
198 | * | 202 | * |
199 | * Returns 0 if there is a process keyring by the end of this function, some | 203 | * Return: 0 if a process keyring is now present; -errno on failure. |
200 | * error otherwise. | ||
201 | */ | 204 | */ |
202 | static int install_process_keyring(void) | 205 | static int install_process_keyring(void) |
203 | { | 206 | { |
@@ -211,14 +214,18 @@ static int install_process_keyring(void) | |||
211 | ret = install_process_keyring_to_cred(new); | 214 | ret = install_process_keyring_to_cred(new); |
212 | if (ret < 0) { | 215 | if (ret < 0) { |
213 | abort_creds(new); | 216 | abort_creds(new); |
214 | return ret != -EEXIST ? ret : 0; | 217 | return ret; |
215 | } | 218 | } |
216 | 219 | ||
217 | return commit_creds(new); | 220 | return commit_creds(new); |
218 | } | 221 | } |
219 | 222 | ||
220 | /* | 223 | /* |
221 | * Install a session keyring directly to a credentials struct. | 224 | * Install the given keyring as the session keyring of the given credentials |
225 | * struct, replacing the existing one if any. If the given keyring is NULL, | ||
226 | * then install a new anonymous session keyring. | ||
227 | * | ||
228 | * Return: 0 on success; -errno on failure. | ||
222 | */ | 229 | */ |
223 | int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) | 230 | int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) |
224 | { | 231 | { |
@@ -253,8 +260,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) | |||
253 | } | 260 | } |
254 | 261 | ||
255 | /* | 262 | /* |
256 | * Install a session keyring, discarding the old one. If a keyring is not | 263 | * Install the given keyring as the session keyring of the current task, |
257 | * supplied, an empty one is invented. | 264 | * replacing the existing one if any. If the given keyring is NULL, then |
265 | * install a new anonymous session keyring. | ||
266 | * | ||
267 | * Return: 0 on success; -errno on failure. | ||
258 | */ | 268 | */ |
259 | static int install_session_keyring(struct key *keyring) | 269 | static int install_session_keyring(struct key *keyring) |
260 | { | 270 | { |