Skip to content

Commit

Permalink
KEYS: fix cred refcount leak in request_key_auth_new()
Browse files Browse the repository at this point in the history
In request_key_auth_new(), if key_alloc() or key_instantiate_and_link()
were to fail, we would leak a reference to the 'struct cred'.  Currently
this can only happen if key_alloc() fails to allocate memory.  But it
still should be fixed, as it is a more severe bug waiting to happen.

Fix it by cleaning things up to use a helper function which frees a
'struct request_key_auth' correctly.

Fixes: d84f4f9 ("CRED: Inaugurate COW credentials")
Signed-off-by: Eric Biggers <[email protected]>
Signed-off-by: David Howells <[email protected]>
  • Loading branch information
ebiggers authored and dhowells committed Sep 25, 2017
1 parent ebb2c24 commit 44d8143
Showing 1 changed file with 31 additions and 37 deletions.
68 changes: 31 additions & 37 deletions security/keys/request_key_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ static void request_key_auth_revoke(struct key *key)
}
}

static void free_request_key_auth(struct request_key_auth *rka)
{
if (!rka)
return;
key_put(rka->target_key);
key_put(rka->dest_keyring);
if (rka->cred)
put_cred(rka->cred);
kfree(rka->callout_info);
kfree(rka);
}

/*
* Destroy an instantiation authorisation token key.
*/
Expand All @@ -129,15 +141,7 @@ static void request_key_auth_destroy(struct key *key)

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

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

key_put(rka->target_key);
key_put(rka->dest_keyring);
kfree(rka->callout_info);
kfree(rka);
free_request_key_auth(rka);
}

/*
Expand All @@ -151,22 +155,17 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
const struct cred *cred = current->cred;
struct key *authkey = NULL;
char desc[20];
int ret;
int ret = -ENOMEM;

kenter("%d,", target->serial);

/* allocate a auth record */
rka = kmalloc(sizeof(*rka), GFP_KERNEL);
if (!rka) {
kleave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
rka = kzalloc(sizeof(*rka), GFP_KERNEL);
if (!rka)
goto error;
rka->callout_info = kmalloc(callout_len, GFP_KERNEL);
if (!rka->callout_info) {
kleave(" = -ENOMEM");
kfree(rka);
return ERR_PTR(-ENOMEM);
}
if (!rka->callout_info)
goto error_free_rka;

/* see if the calling process is already servicing the key request of
* another process */
Expand All @@ -176,8 +175,12 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,

/* if the auth key has been revoked, then the key we're
* servicing is already instantiated */
if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags))
goto auth_key_revoked;
if (test_bit(KEY_FLAG_REVOKED,
&cred->request_key_auth->flags)) {
up_read(&cred->request_key_auth->sem);
ret = -EKEYREVOKED;
goto error_free_rka;
}

irka = cred->request_key_auth->payload.data[0];
rka->cred = get_cred(irka->cred);
Expand Down Expand Up @@ -205,32 +208,23 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
goto error_alloc;
goto error_free_rka;
}

/* construct the auth key */
ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
if (ret < 0)
goto error_inst;
goto error_put_authkey;

kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
return authkey;

auth_key_revoked:
up_read(&cred->request_key_auth->sem);
kfree(rka->callout_info);
kfree(rka);
kleave("= -EKEYREVOKED");
return ERR_PTR(-EKEYREVOKED);

error_inst:
error_put_authkey:
key_revoke(authkey);
key_put(authkey);
error_alloc:
key_put(rka->target_key);
key_put(rka->dest_keyring);
kfree(rka->callout_info);
kfree(rka);
error_free_rka:
free_request_key_auth(rka);
error:
kleave("= %d", ret);
return ERR_PTR(ret);
}
Expand Down

0 comments on commit 44d8143

Please sign in to comment.