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 | |
| 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>
| -rw-r--r-- | include/linux/sched/user.h | 7 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 31 | ||||
| -rw-r--r-- | security/keys/request_key.c | 5 |
3 files changed, 27 insertions, 16 deletions
diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h index c7b5f86b91a1..468d2565a9fe 100644 --- a/include/linux/sched/user.h +++ b/include/linux/sched/user.h | |||
| @@ -31,6 +31,13 @@ struct user_struct { | |||
| 31 | atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ | 31 | atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ |
| 32 | 32 | ||
| 33 | #ifdef CONFIG_KEYS | 33 | #ifdef CONFIG_KEYS |
| 34 | /* | ||
| 35 | * These pointers can only change from NULL to a non-NULL value once. | ||
| 36 | * Writes are protected by key_user_keyring_mutex. | ||
| 37 | * Unlocked readers should use READ_ONCE() unless they know that | ||
| 38 | * install_user_keyrings() has been called successfully (which sets | ||
| 39 | * these members to non-NULL values, preventing further modifications). | ||
| 40 | */ | ||
| 34 | struct key *uid_keyring; /* UID specific keyring */ | 41 | struct key *uid_keyring; /* UID specific keyring */ |
| 35 | struct key *session_keyring; /* UID's default session keyring */ | 42 | struct key *session_keyring; /* UID's default session keyring */ |
| 36 | #endif | 43 | #endif |
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: |
