diff options
author | Jann Horn <jannh@google.com> | 2019-03-27 11:55:08 -0400 |
---|---|---|
committer | James Morris <james.morris@microsoft.com> | 2019-04-10 13:29:50 -0400 |
commit | 0b9dc6c9f01c4a726558b82a3b6082a89d264eb5 (patch) | |
tree | 1acfa81de568fa53ee14fac6219a140cd6560778 /security | |
parent | 5c7e372caa35d303e414caeb64ee2243fd3cac3d (diff) |
keys: safe concurrent user->{session,uid}_keyring access
The current code can perform concurrent updates and reads on
user->session_keyring and user->uid_keyring. Add a comment to
struct user_struct to document the nontrivial locking semantics, and use
READ_ONCE() for unlocked readers and smp_store_release() for writers to
prevent memory ordering issues.
Fixes: 69664cf16af4 ("keys: don't generate user and user session keyrings unless they're accessed")
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: James Morris <james.morris@microsoft.com>
Diffstat (limited to 'security')
-rw-r--r-- | security/keys/process_keys.c | 31 | ||||
-rw-r--r-- | security/keys/request_key.c | 5 |
2 files changed, 20 insertions, 16 deletions
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index bd7243cb4c85..f05f7125a7d5 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
@@ -58,7 +58,7 @@ int install_user_keyrings(void) | |||
58 | 58 | ||
59 | kenter("%p{%u}", user, uid); | 59 | kenter("%p{%u}", user, uid); |
60 | 60 | ||
61 | if (user->uid_keyring && user->session_keyring) { | 61 | if (READ_ONCE(user->uid_keyring) && READ_ONCE(user->session_keyring)) { |
62 | kleave(" = 0 [exist]"); | 62 | kleave(" = 0 [exist]"); |
63 | return 0; | 63 | return 0; |
64 | } | 64 | } |
@@ -111,8 +111,10 @@ int install_user_keyrings(void) | |||
111 | } | 111 | } |
112 | 112 | ||
113 | /* install the keyrings */ | 113 | /* install the keyrings */ |
114 | user->uid_keyring = uid_keyring; | 114 | /* paired with READ_ONCE() */ |
115 | user->session_keyring = session_keyring; | 115 | smp_store_release(&user->uid_keyring, uid_keyring); |
116 | /* paired with READ_ONCE() */ | ||
117 | smp_store_release(&user->session_keyring, session_keyring); | ||
116 | } | 118 | } |
117 | 119 | ||
118 | mutex_unlock(&key_user_keyring_mutex); | 120 | mutex_unlock(&key_user_keyring_mutex); |
@@ -340,6 +342,7 @@ void key_fsgid_changed(struct task_struct *tsk) | |||
340 | key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) | 342 | key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) |
341 | { | 343 | { |
342 | key_ref_t key_ref, ret, err; | 344 | key_ref_t key_ref, ret, err; |
345 | const struct cred *cred = ctx->cred; | ||
343 | 346 | ||
344 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were | 347 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were |
345 | * searchable, but we failed to find a key or we found a negative key; | 348 | * searchable, but we failed to find a key or we found a negative key; |
@@ -353,9 +356,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) | |||
353 | err = ERR_PTR(-EAGAIN); | 356 | err = ERR_PTR(-EAGAIN); |
354 | 357 | ||
355 | /* search the thread keyring first */ | 358 | /* search the thread keyring first */ |
356 | if (ctx->cred->thread_keyring) { | 359 | if (cred->thread_keyring) { |
357 | key_ref = keyring_search_aux( | 360 | key_ref = keyring_search_aux( |
358 | make_key_ref(ctx->cred->thread_keyring, 1), ctx); | 361 | make_key_ref(cred->thread_keyring, 1), ctx); |
359 | if (!IS_ERR(key_ref)) | 362 | if (!IS_ERR(key_ref)) |
360 | goto found; | 363 | goto found; |
361 | 364 | ||
@@ -371,9 +374,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) | |||
371 | } | 374 | } |
372 | 375 | ||
373 | /* search the process keyring second */ | 376 | /* search the process keyring second */ |
374 | if (ctx->cred->process_keyring) { | 377 | if (cred->process_keyring) { |
375 | key_ref = keyring_search_aux( | 378 | key_ref = keyring_search_aux( |
376 | make_key_ref(ctx->cred->process_keyring, 1), ctx); | 379 | make_key_ref(cred->process_keyring, 1), ctx); |
377 | if (!IS_ERR(key_ref)) | 380 | if (!IS_ERR(key_ref)) |
378 | goto found; | 381 | goto found; |
379 | 382 | ||
@@ -392,9 +395,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) | |||
392 | } | 395 | } |
393 | 396 | ||
394 | /* search the session keyring */ | 397 | /* search the session keyring */ |
395 | if (ctx->cred->session_keyring) { | 398 | if (cred->session_keyring) { |
396 | key_ref = keyring_search_aux( | 399 | key_ref = keyring_search_aux( |
397 | make_key_ref(ctx->cred->session_keyring, 1), ctx); | 400 | make_key_ref(cred->session_keyring, 1), ctx); |
398 | 401 | ||
399 | if (!IS_ERR(key_ref)) | 402 | if (!IS_ERR(key_ref)) |
400 | goto found; | 403 | goto found; |
@@ -413,9 +416,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) | |||
413 | } | 416 | } |
414 | } | 417 | } |
415 | /* or search the user-session keyring */ | 418 | /* or search the user-session keyring */ |
416 | else if (ctx->cred->user->session_keyring) { | 419 | else if (READ_ONCE(cred->user->session_keyring)) { |
417 | key_ref = keyring_search_aux( | 420 | key_ref = keyring_search_aux( |
418 | make_key_ref(ctx->cred->user->session_keyring, 1), | 421 | make_key_ref(READ_ONCE(cred->user->session_keyring), 1), |
419 | ctx); | 422 | ctx); |
420 | if (!IS_ERR(key_ref)) | 423 | if (!IS_ERR(key_ref)) |
421 | goto found; | 424 | goto found; |
@@ -602,7 +605,7 @@ try_again: | |||
602 | goto error; | 605 | goto error; |
603 | goto reget_creds; | 606 | goto reget_creds; |
604 | } else if (ctx.cred->session_keyring == | 607 | } else if (ctx.cred->session_keyring == |
605 | ctx.cred->user->session_keyring && | 608 | READ_ONCE(ctx.cred->user->session_keyring) && |
606 | lflags & KEY_LOOKUP_CREATE) { | 609 | lflags & KEY_LOOKUP_CREATE) { |
607 | ret = join_session_keyring(NULL); | 610 | ret = join_session_keyring(NULL); |
608 | if (ret < 0) | 611 | if (ret < 0) |
@@ -616,7 +619,7 @@ try_again: | |||
616 | break; | 619 | break; |
617 | 620 | ||
618 | case KEY_SPEC_USER_KEYRING: | 621 | case KEY_SPEC_USER_KEYRING: |
619 | if (!ctx.cred->user->uid_keyring) { | 622 | if (!READ_ONCE(ctx.cred->user->uid_keyring)) { |
620 | ret = install_user_keyrings(); | 623 | ret = install_user_keyrings(); |
621 | if (ret < 0) | 624 | if (ret < 0) |
622 | goto error; | 625 | goto error; |
@@ -628,7 +631,7 @@ try_again: | |||
628 | break; | 631 | break; |
629 | 632 | ||
630 | case KEY_SPEC_USER_SESSION_KEYRING: | 633 | case KEY_SPEC_USER_SESSION_KEYRING: |
631 | if (!ctx.cred->user->session_keyring) { | 634 | if (!READ_ONCE(ctx.cred->user->session_keyring)) { |
632 | ret = install_user_keyrings(); | 635 | ret = install_user_keyrings(); |
633 | if (ret < 0) | 636 | if (ret < 0) |
634 | goto error; | 637 | goto error; |
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index db72dc4d7639..75d87f9e0f49 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
@@ -293,11 +293,12 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) | |||
293 | /* fall through */ | 293 | /* fall through */ |
294 | case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: | 294 | case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: |
295 | dest_keyring = | 295 | dest_keyring = |
296 | key_get(cred->user->session_keyring); | 296 | key_get(READ_ONCE(cred->user->session_keyring)); |
297 | break; | 297 | break; |
298 | 298 | ||
299 | case KEY_REQKEY_DEFL_USER_KEYRING: | 299 | case KEY_REQKEY_DEFL_USER_KEYRING: |
300 | dest_keyring = key_get(cred->user->uid_keyring); | 300 | dest_keyring = |
301 | key_get(READ_ONCE(cred->user->uid_keyring)); | ||
301 | break; | 302 | break; |
302 | 303 | ||
303 | case KEY_REQKEY_DEFL_GROUP_KEYRING: | 304 | case KEY_REQKEY_DEFL_GROUP_KEYRING: |