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: d84f4f992c ("CRED: Inaugurate COW credentials")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
pull/473/head
parent
ebb2c2437d
commit
44d8143340
|
|
@ -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)
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
/* 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,
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue