Skip to content

Commit

Permalink
keys: Move the RCU locks outwards from the keyring search functions
Browse files Browse the repository at this point in the history
Move the RCU locks outwards from the keyring search functions so that it
will become possible to provide an RCU-capable partial request_key()
function in a later commit.

Signed-off-by: David Howells <[email protected]>
  • Loading branch information
dhowells committed Jun 19, 2019
1 parent a09003b commit e59428f
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Documentation/security/keys/request-key.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ The Search Algorithm

A search of any particular keyring proceeds in the following fashion:

1) When the key management code searches for a key (keyring_search_aux) it
1) When the key management code searches for a key (keyring_search_rcu) it
firstly calls key_permission(SEARCH) on the keyring it's starting with,
if this denies permission, it doesn't search further.

Expand Down
1 change: 1 addition & 0 deletions include/keys/request_key_auth-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Authorisation record for request_key().
*/
struct request_key_auth {
struct rcu_head rcu;
struct key *target_key;
struct key *dest_keyring;
const struct cred *cred;
Expand Down
6 changes: 3 additions & 3 deletions security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@ struct keyring_search_context {

extern bool key_default_cmp(const struct key *key,
const struct key_match_data *match_data);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
extern key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
struct keyring_search_context *ctx);

extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
extern key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx);
extern key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx);

extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);

Expand Down
16 changes: 9 additions & 7 deletions security/keys/keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ static bool search_nested_keyrings(struct key *keyring,
}

/**
* keyring_search_aux - Search a keyring tree for a key matching some criteria
* keyring_search_rcu - Search a keyring tree for a matching key under RCU
* @keyring_ref: A pointer to the keyring with possession indicator.
* @ctx: The keyring search context.
*
Expand All @@ -847,7 +847,9 @@ static bool search_nested_keyrings(struct key *keyring,
* addition, the LSM gets to forbid keyring searches and key matches.
*
* The search is performed as a breadth-then-depth search up to the prescribed
* limit (KEYRING_SEARCH_MAX_DEPTH).
* limit (KEYRING_SEARCH_MAX_DEPTH). The caller must hold the RCU read lock to
* prevent keyrings from being destroyed or rearranged whilst they are being
* searched.
*
* Keys are matched to the type provided and are then filtered by the match
* function, which is given the description to use in any way it sees fit. The
Expand All @@ -866,7 +868,7 @@ static bool search_nested_keyrings(struct key *keyring,
* In the case of a successful return, the possession attribute from
* @keyring_ref is propagated to the returned key reference.
*/
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
struct keyring_search_context *ctx)
{
struct key *keyring;
Expand All @@ -888,11 +890,9 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
return ERR_PTR(err);
}

rcu_read_lock();
ctx->now = ktime_get_real_seconds();
if (search_nested_keyrings(keyring, ctx))
__key_get(key_ref_to_ptr(ctx->result));
rcu_read_unlock();
return ctx->result;
}

Expand All @@ -902,7 +902,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
* @type: The type of keyring we want to find.
* @description: The name of the keyring we want to find.
*
* As keyring_search_aux() above, but using the current task's credentials and
* As keyring_search_rcu() above, but using the current task's credentials and
* type's default matching function and preferred search method.
*/
key_ref_t keyring_search(key_ref_t keyring,
Expand All @@ -928,7 +928,9 @@ key_ref_t keyring_search(key_ref_t keyring,
return ERR_PTR(ret);
}

key = keyring_search_aux(keyring, &ctx);
rcu_read_lock();
key = keyring_search_rcu(keyring, &ctx);
rcu_read_unlock();

if (type->match_free)
type->match_free(&ctx.match_data);
Expand Down
4 changes: 3 additions & 1 deletion security/keys/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ static int proc_keys_show(struct seq_file *m, void *v)
* skip if the key does not indicate the possessor can view it
*/
if (key->perm & KEY_POS_VIEW) {
skey_ref = search_my_process_keyrings(&ctx);
rcu_read_lock();
skey_ref = search_cred_keyrings_rcu(&ctx);
rcu_read_unlock();
if (!IS_ERR(skey_ref)) {
key_ref_put(skey_ref);
key_ref = make_key_ref(key, 1);
Expand Down
41 changes: 18 additions & 23 deletions security/keys/process_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ void key_fsgid_changed(struct cred *new_cred)

/*
* Search the process keyrings attached to the supplied cred for the first
* matching key.
* matching key under RCU conditions (the caller must be holding the RCU read
* lock).
*
* The search criteria are the type and the match function. The description is
* given to the match function as a parameter, but doesn't otherwise influence
Expand All @@ -337,7 +338,7 @@ void key_fsgid_changed(struct cred *new_cred)
* In the case of a successful return, the possession attribute is set on the
* returned key reference.
*/
key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx)
{
key_ref_t key_ref, ret, err;
const struct cred *cred = ctx->cred;
Expand All @@ -355,7 +356,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)

/* search the thread keyring first */
if (cred->thread_keyring) {
key_ref = keyring_search_aux(
key_ref = keyring_search_rcu(
make_key_ref(cred->thread_keyring, 1), ctx);
if (!IS_ERR(key_ref))
goto found;
Expand All @@ -373,7 +374,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)

/* search the process keyring second */
if (cred->process_keyring) {
key_ref = keyring_search_aux(
key_ref = keyring_search_rcu(
make_key_ref(cred->process_keyring, 1), ctx);
if (!IS_ERR(key_ref))
goto found;
Expand All @@ -394,7 +395,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)

/* search the session keyring */
if (cred->session_keyring) {
key_ref = keyring_search_aux(
key_ref = keyring_search_rcu(
make_key_ref(cred->session_keyring, 1), ctx);

if (!IS_ERR(key_ref))
Expand All @@ -415,7 +416,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
}
/* or search the user-session keyring */
else if (READ_ONCE(cred->user->session_keyring)) {
key_ref = keyring_search_aux(
key_ref = keyring_search_rcu(
make_key_ref(READ_ONCE(cred->user->session_keyring), 1),
ctx);
if (!IS_ERR(key_ref))
Expand Down Expand Up @@ -448,16 +449,16 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
* the keys attached to the assumed authorisation key using its credentials if
* one is available.
*
* Return same as search_my_process_keyrings().
* The caller must be holding the RCU read lock.
*
* Return same as search_cred_keyrings_rcu().
*/
key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;

might_sleep();

key_ref = search_my_process_keyrings(ctx);
key_ref = search_cred_keyrings_rcu(ctx);
if (!IS_ERR(key_ref))
goto found;
err = key_ref;
Expand All @@ -472,24 +473,17 @@ key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
) {
const struct cred *cred = ctx->cred;

/* defend against the auth key being revoked */
down_read(&cred->request_key_auth->sem);

if (key_validate(ctx->cred->request_key_auth) == 0) {
if (key_validate(cred->request_key_auth) == 0) {
rka = ctx->cred->request_key_auth->payload.data[0];

//// was search_process_keyrings() [ie. recursive]
ctx->cred = rka->cred;
key_ref = search_process_keyrings(ctx);
key_ref = search_cred_keyrings_rcu(ctx);
ctx->cred = cred;

up_read(&cred->request_key_auth->sem);

if (!IS_ERR(key_ref))
goto found;

ret = key_ref;
} else {
up_read(&cred->request_key_auth->sem);
}
}

Expand All @@ -504,7 +498,6 @@ key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
found:
return key_ref;
}

/*
* See if the key we're looking at is the target key.
*/
Expand Down Expand Up @@ -691,7 +684,9 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
ctx.index_key = key->index_key;
ctx.match_data.raw_data = key;
kdebug("check possessed");
skey_ref = search_process_keyrings(&ctx);
rcu_read_lock();
skey_ref = search_process_keyrings_rcu(&ctx);
rcu_read_unlock();
kdebug("possessed=%p", skey_ref);

if (!IS_ERR(skey_ref)) {
Expand Down
8 changes: 6 additions & 2 deletions security/keys/request_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,9 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
* waited for locks */
mutex_lock(&key_construction_mutex);

key_ref = search_process_keyrings(ctx);
rcu_read_lock();
key_ref = search_process_keyrings_rcu(ctx);
rcu_read_unlock();
if (!IS_ERR(key_ref))
goto key_already_present;

Expand Down Expand Up @@ -561,7 +563,9 @@ struct key *request_key_and_link(struct key_type *type,
}

/* search all the process keyrings for a key */
key_ref = search_process_keyrings(&ctx);
rcu_read_lock();
key_ref = search_process_keyrings_rcu(&ctx);
rcu_read_unlock();

if (!IS_ERR(key_ref)) {
if (dest_keyring) {
Expand Down
60 changes: 36 additions & 24 deletions security/keys/request_key_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
static int request_key_auth_instantiate(struct key *key,
struct key_preparsed_payload *prep)
{
key->payload.data[0] = (struct request_key_auth *)prep->data;
rcu_assign_keypointer(key, (struct request_key_auth *)prep->data);
return 0;
}

Expand All @@ -68,7 +68,7 @@ static int request_key_auth_instantiate(struct key *key,
static void request_key_auth_describe(const struct key *key,
struct seq_file *m)
{
struct request_key_auth *rka = get_request_key_auth(key);
struct request_key_auth *rka = dereference_key_rcu(key);

seq_puts(m, "key:");
seq_puts(m, key->description);
Expand All @@ -83,7 +83,7 @@ static void request_key_auth_describe(const struct key *key,
static long request_key_auth_read(const struct key *key,
char __user *buffer, size_t buflen)
{
struct request_key_auth *rka = get_request_key_auth(key);
struct request_key_auth *rka = dereference_key_locked(key);
size_t datalen;
long ret;

Expand All @@ -102,23 +102,6 @@ static long request_key_auth_read(const struct key *key,
return ret;
}

/*
* Handle revocation of an authorisation token key.
*
* Called with the key sem write-locked.
*/
static void request_key_auth_revoke(struct key *key)
{
struct request_key_auth *rka = get_request_key_auth(key);

kenter("{%d}", key->serial);

if (rka->cred) {
put_cred(rka->cred);
rka->cred = NULL;
}
}

static void free_request_key_auth(struct request_key_auth *rka)
{
if (!rka)
Expand All @@ -131,16 +114,43 @@ static void free_request_key_auth(struct request_key_auth *rka)
kfree(rka);
}

/*
* Dispose of the request_key_auth record under RCU conditions
*/
static void request_key_auth_rcu_disposal(struct rcu_head *rcu)
{
struct request_key_auth *rka =
container_of(rcu, struct request_key_auth, rcu);

free_request_key_auth(rka);
}

/*
* Handle revocation of an authorisation token key.
*
* Called with the key sem write-locked.
*/
static void request_key_auth_revoke(struct key *key)
{
struct request_key_auth *rka = dereference_key_locked(key);

kenter("{%d}", key->serial);
rcu_assign_keypointer(key, NULL);
call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
}

/*
* Destroy an instantiation authorisation token key.
*/
static void request_key_auth_destroy(struct key *key)
{
struct request_key_auth *rka = get_request_key_auth(key);
struct request_key_auth *rka = rcu_access_pointer(key->payload.rcu_data0);

kenter("{%d}", key->serial);

free_request_key_auth(rka);
if (rka) {
rcu_assign_keypointer(key, NULL);
call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
}
}

/*
Expand Down Expand Up @@ -249,7 +259,9 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)

ctx.index_key.desc_len = sprintf(description, "%x", target_id);

authkey_ref = search_process_keyrings(&ctx);
rcu_read_lock();
authkey_ref = search_process_keyrings_rcu(&ctx);
rcu_read_unlock();

if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
Expand Down

0 comments on commit e59428f

Please sign in to comment.