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: 69664cf16a
("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>
This commit is contained in:
parent
5c7e372caa
commit
0b9dc6c9f0
|
@ -31,6 +31,13 @@ struct user_struct {
|
||||||
atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */
|
atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */
|
||||||
|
|
||||||
#ifdef CONFIG_KEYS
|
#ifdef CONFIG_KEYS
|
||||||
|
/*
|
||||||
|
* These pointers can only change from NULL to a non-NULL value once.
|
||||||
|
* Writes are protected by key_user_keyring_mutex.
|
||||||
|
* Unlocked readers should use READ_ONCE() unless they know that
|
||||||
|
* install_user_keyrings() has been called successfully (which sets
|
||||||
|
* these members to non-NULL values, preventing further modifications).
|
||||||
|
*/
|
||||||
struct key *uid_keyring; /* UID specific keyring */
|
struct key *uid_keyring; /* UID specific keyring */
|
||||||
struct key *session_keyring; /* UID's default session keyring */
|
struct key *session_keyring; /* UID's default session keyring */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -58,7 +58,7 @@ int install_user_keyrings(void)
|
||||||
|
|
||||||
kenter("%p{%u}", user, uid);
|
kenter("%p{%u}", user, uid);
|
||||||
|
|
||||||
if (user->uid_keyring && user->session_keyring) {
|
if (READ_ONCE(user->uid_keyring) && READ_ONCE(user->session_keyring)) {
|
||||||
kleave(" = 0 [exist]");
|
kleave(" = 0 [exist]");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -111,8 +111,10 @@ int install_user_keyrings(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* install the keyrings */
|
/* install the keyrings */
|
||||||
user->uid_keyring = uid_keyring;
|
/* paired with READ_ONCE() */
|
||||||
user->session_keyring = session_keyring;
|
smp_store_release(&user->uid_keyring, uid_keyring);
|
||||||
|
/* paired with READ_ONCE() */
|
||||||
|
smp_store_release(&user->session_keyring, session_keyring);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&key_user_keyring_mutex);
|
mutex_unlock(&key_user_keyring_mutex);
|
||||||
|
@ -340,6 +342,7 @@ void key_fsgid_changed(struct task_struct *tsk)
|
||||||
key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||||
{
|
{
|
||||||
key_ref_t key_ref, ret, err;
|
key_ref_t key_ref, ret, err;
|
||||||
|
const struct cred *cred = ctx->cred;
|
||||||
|
|
||||||
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
|
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
|
||||||
* searchable, but we failed to find a key or we found a negative key;
|
* 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)
|
||||||
err = ERR_PTR(-EAGAIN);
|
err = ERR_PTR(-EAGAIN);
|
||||||
|
|
||||||
/* search the thread keyring first */
|
/* search the thread keyring first */
|
||||||
if (ctx->cred->thread_keyring) {
|
if (cred->thread_keyring) {
|
||||||
key_ref = keyring_search_aux(
|
key_ref = keyring_search_aux(
|
||||||
make_key_ref(ctx->cred->thread_keyring, 1), ctx);
|
make_key_ref(cred->thread_keyring, 1), ctx);
|
||||||
if (!IS_ERR(key_ref))
|
if (!IS_ERR(key_ref))
|
||||||
goto found;
|
goto found;
|
||||||
|
|
||||||
|
@ -371,9 +374,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* search the process keyring second */
|
/* search the process keyring second */
|
||||||
if (ctx->cred->process_keyring) {
|
if (cred->process_keyring) {
|
||||||
key_ref = keyring_search_aux(
|
key_ref = keyring_search_aux(
|
||||||
make_key_ref(ctx->cred->process_keyring, 1), ctx);
|
make_key_ref(cred->process_keyring, 1), ctx);
|
||||||
if (!IS_ERR(key_ref))
|
if (!IS_ERR(key_ref))
|
||||||
goto found;
|
goto found;
|
||||||
|
|
||||||
|
@ -392,9 +395,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* search the session keyring */
|
/* search the session keyring */
|
||||||
if (ctx->cred->session_keyring) {
|
if (cred->session_keyring) {
|
||||||
key_ref = keyring_search_aux(
|
key_ref = keyring_search_aux(
|
||||||
make_key_ref(ctx->cred->session_keyring, 1), ctx);
|
make_key_ref(cred->session_keyring, 1), ctx);
|
||||||
|
|
||||||
if (!IS_ERR(key_ref))
|
if (!IS_ERR(key_ref))
|
||||||
goto found;
|
goto found;
|
||||||
|
@ -413,9 +416,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* or search the user-session keyring */
|
/* or search the user-session keyring */
|
||||||
else if (ctx->cred->user->session_keyring) {
|
else if (READ_ONCE(cred->user->session_keyring)) {
|
||||||
key_ref = keyring_search_aux(
|
key_ref = keyring_search_aux(
|
||||||
make_key_ref(ctx->cred->user->session_keyring, 1),
|
make_key_ref(READ_ONCE(cred->user->session_keyring), 1),
|
||||||
ctx);
|
ctx);
|
||||||
if (!IS_ERR(key_ref))
|
if (!IS_ERR(key_ref))
|
||||||
goto found;
|
goto found;
|
||||||
|
@ -602,7 +605,7 @@ try_again:
|
||||||
goto error;
|
goto error;
|
||||||
goto reget_creds;
|
goto reget_creds;
|
||||||
} else if (ctx.cred->session_keyring ==
|
} else if (ctx.cred->session_keyring ==
|
||||||
ctx.cred->user->session_keyring &&
|
READ_ONCE(ctx.cred->user->session_keyring) &&
|
||||||
lflags & KEY_LOOKUP_CREATE) {
|
lflags & KEY_LOOKUP_CREATE) {
|
||||||
ret = join_session_keyring(NULL);
|
ret = join_session_keyring(NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -616,7 +619,7 @@ try_again:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_SPEC_USER_KEYRING:
|
case KEY_SPEC_USER_KEYRING:
|
||||||
if (!ctx.cred->user->uid_keyring) {
|
if (!READ_ONCE(ctx.cred->user->uid_keyring)) {
|
||||||
ret = install_user_keyrings();
|
ret = install_user_keyrings();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -628,7 +631,7 @@ try_again:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_SPEC_USER_SESSION_KEYRING:
|
case KEY_SPEC_USER_SESSION_KEYRING:
|
||||||
if (!ctx.cred->user->session_keyring) {
|
if (!READ_ONCE(ctx.cred->user->session_keyring)) {
|
||||||
ret = install_user_keyrings();
|
ret = install_user_keyrings();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
@ -293,11 +293,12 @@ static int construct_get_dest_keyring(struct key **_dest_keyring)
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
|
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
|
||||||
dest_keyring =
|
dest_keyring =
|
||||||
key_get(cred->user->session_keyring);
|
key_get(READ_ONCE(cred->user->session_keyring));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_REQKEY_DEFL_USER_KEYRING:
|
case KEY_REQKEY_DEFL_USER_KEYRING:
|
||||||
dest_keyring = key_get(cred->user->uid_keyring);
|
dest_keyring =
|
||||||
|
key_get(READ_ONCE(cred->user->uid_keyring));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_REQKEY_DEFL_GROUP_KEYRING:
|
case KEY_REQKEY_DEFL_GROUP_KEYRING:
|
||||||
|
|
Loading…
Reference in New Issue