Commit 44d81433 authored by Eric Biggers's avatar Eric Biggers Committed by David Howells

KEYS: fix cred refcount leak in request_key_auth_new()

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: d84f4f99 ("CRED: Inaugurate COW credentials")
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent ebb2c243
...@@ -120,6 +120,18 @@ static void request_key_auth_revoke(struct key *key) ...@@ -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. * Destroy an instantiation authorisation token key.
*/ */
...@@ -129,15 +141,7 @@ static void request_key_auth_destroy(struct key *key) ...@@ -129,15 +141,7 @@ static void request_key_auth_destroy(struct key *key)
kenter("{%d}", key->serial); kenter("{%d}", key->serial);
if (rka->cred) { free_request_key_auth(rka);
put_cred(rka->cred);
rka->cred = NULL;
}
key_put(rka->target_key);
key_put(rka->dest_keyring);
kfree(rka->callout_info);
kfree(rka);
} }
/* /*
...@@ -151,22 +155,17 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, ...@@ -151,22 +155,17 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
const struct cred *cred = current->cred; const struct cred *cred = current->cred;
struct key *authkey = NULL; struct key *authkey = NULL;
char desc[20]; char desc[20];
int ret; int ret = -ENOMEM;
kenter("%d,", target->serial); kenter("%d,", target->serial);
/* allocate a auth record */ /* allocate a auth record */
rka = kmalloc(sizeof(*rka), GFP_KERNEL); rka = kzalloc(sizeof(*rka), GFP_KERNEL);
if (!rka) { if (!rka)
kleave(" = -ENOMEM"); goto error;
return ERR_PTR(-ENOMEM);
}
rka->callout_info = kmalloc(callout_len, GFP_KERNEL); rka->callout_info = kmalloc(callout_len, GFP_KERNEL);
if (!rka->callout_info) { if (!rka->callout_info)
kleave(" = -ENOMEM"); goto error_free_rka;
kfree(rka);
return ERR_PTR(-ENOMEM);
}
/* see if the calling process is already servicing the key request of /* see if the calling process is already servicing the key request of
* another process */ * another process */
...@@ -176,8 +175,12 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, ...@@ -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 /* if the auth key has been revoked, then the key we're
* servicing is already instantiated */ * servicing is already instantiated */
if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags)) if (test_bit(KEY_FLAG_REVOKED,
goto auth_key_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]; irka = cred->request_key_auth->payload.data[0];
rka->cred = get_cred(irka->cred); rka->cred = get_cred(irka->cred);
...@@ -205,32 +208,23 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, ...@@ -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); KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) { if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey); ret = PTR_ERR(authkey);
goto error_alloc; goto error_free_rka;
} }
/* construct the auth key */ /* construct the auth key */
ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL); ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
if (ret < 0) if (ret < 0)
goto error_inst; goto error_put_authkey;
kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage)); kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
return authkey; return authkey;
auth_key_revoked: error_put_authkey:
up_read(&cred->request_key_auth->sem);
kfree(rka->callout_info);
kfree(rka);
kleave("= -EKEYREVOKED");
return ERR_PTR(-EKEYREVOKED);
error_inst:
key_revoke(authkey); key_revoke(authkey);
key_put(authkey); key_put(authkey);
error_alloc: error_free_rka:
key_put(rka->target_key); free_request_key_auth(rka);
key_put(rka->dest_keyring); error:
kfree(rka->callout_info);
kfree(rka);
kleave("= %d", ret); kleave("= %d", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment