Commit cab8eb59 authored by David Howells's avatar David Howells Committed by Linus Torvalds

[PATCH] keys: Discard duplicate keys from a keyring on link

Cause any links within a keyring to keys that match a key to be linked into
that keyring to be discarded as a link to the new key is added.  The match is
contingent on the type and description strings being the same.

This permits requests, adds and searches to displace negative, expired,
revoked and dead keys easily.  After some discussion it was concluded that
duplicate valid keys should probably be discarded also as they would otherwise
hide the new key.

Since request_key() is intended to be the primary method by which keys are
added to a keyring, duplicate valid keys wouldn't be an issue there as that
function would return an existing match in preference to creating a new key.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Cc: Alexander Zangerl <az@bond.edu.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 017679c4
...@@ -500,6 +500,10 @@ The keyctl syscall functions are: ...@@ -500,6 +500,10 @@ The keyctl syscall functions are:
The link procedure checks the nesting of the keyrings, returning ELOOP if The link procedure checks the nesting of the keyrings, returning ELOOP if
it appears too deep or EDEADLK if the link would introduce a cycle. it appears too deep or EDEADLK if the link would introduce a cycle.
Any links within the keyring to keys that match the new key in terms of
type and description will be discarded from the keyring as the new one is
added.
(*) Unlink a key or keyring from another keyring: (*) Unlink a key or keyring from another keyring:
......
...@@ -682,17 +682,33 @@ static void keyring_link_rcu_disposal(struct rcu_head *rcu) ...@@ -682,17 +682,33 @@ static void keyring_link_rcu_disposal(struct rcu_head *rcu)
} /* end keyring_link_rcu_disposal() */ } /* end keyring_link_rcu_disposal() */
/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period, freeing the unlinked
* key
*/
static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
{
struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu);
key_put(klist->keys[klist->delkey]);
kfree(klist);
} /* end keyring_unlink_rcu_disposal() */
/*****************************************************************************/ /*****************************************************************************/
/* /*
* link a key into to a keyring * link a key into to a keyring
* - must be called with the keyring's semaphore write-locked * - must be called with the keyring's semaphore write-locked
* - discard already extant link to matching key if there is one
*/ */
int __key_link(struct key *keyring, struct key *key) int __key_link(struct key *keyring, struct key *key)
{ {
struct keyring_list *klist, *nklist; struct keyring_list *klist, *nklist;
unsigned max; unsigned max;
size_t size; size_t size;
int ret; int loop, ret;
ret = -EKEYREVOKED; ret = -EKEYREVOKED;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
...@@ -714,6 +730,48 @@ int __key_link(struct key *keyring, struct key *key) ...@@ -714,6 +730,48 @@ int __key_link(struct key *keyring, struct key *key)
goto error2; goto error2;
} }
/* see if there's a matching key we can displace */
klist = keyring->payload.subscriptions;
if (klist && klist->nkeys > 0) {
struct key_type *type = key->type;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
if (klist->keys[loop]->type == type &&
strcmp(klist->keys[loop]->description,
key->description) == 0
) {
/* found a match - replace with new key */
size = sizeof(struct key *) * klist->maxkeys;
size += sizeof(*klist);
BUG_ON(size > PAGE_SIZE);
ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL);
if (!nklist)
goto error2;
memcpy(nklist, klist, size);
/* replace matched key */
atomic_inc(&key->usage);
nklist->keys[loop] = key;
rcu_assign_pointer(
keyring->payload.subscriptions,
nklist);
/* dispose of the old keyring list and the
* displaced key */
klist->delkey = loop;
call_rcu(&klist->rcu,
keyring_unlink_rcu_disposal);
goto done;
}
}
}
/* check that we aren't going to overrun the user's quota */ /* check that we aren't going to overrun the user's quota */
ret = key_payload_reserve(keyring, ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES); keyring->datalen + KEYQUOTA_LINK_BYTES);
...@@ -730,8 +788,6 @@ int __key_link(struct key *keyring, struct key *key) ...@@ -730,8 +788,6 @@ int __key_link(struct key *keyring, struct key *key)
smp_wmb(); smp_wmb();
klist->nkeys++; klist->nkeys++;
smp_wmb(); smp_wmb();
ret = 0;
} }
else { else {
/* grow the key list */ /* grow the key list */
...@@ -769,16 +825,16 @@ int __key_link(struct key *keyring, struct key *key) ...@@ -769,16 +825,16 @@ int __key_link(struct key *keyring, struct key *key)
/* dispose of the old keyring list */ /* dispose of the old keyring list */
if (klist) if (klist)
call_rcu(&klist->rcu, keyring_link_rcu_disposal); call_rcu(&klist->rcu, keyring_link_rcu_disposal);
ret = 0;
} }
error2: done:
ret = 0;
error2:
up_write(&keyring_serialise_link_sem); up_write(&keyring_serialise_link_sem);
error: error:
return ret; return ret;
error3: error3:
/* undo the quota changes */ /* undo the quota changes */
key_payload_reserve(keyring, key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES); keyring->datalen - KEYQUOTA_LINK_BYTES);
...@@ -807,21 +863,6 @@ int key_link(struct key *keyring, struct key *key) ...@@ -807,21 +863,6 @@ int key_link(struct key *keyring, struct key *key)
EXPORT_SYMBOL(key_link); EXPORT_SYMBOL(key_link);
/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period, freeing the unlinked
* key
*/
static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
{
struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu);
key_put(klist->keys[klist->delkey]);
kfree(klist);
} /* end keyring_unlink_rcu_disposal() */
/*****************************************************************************/ /*****************************************************************************/
/* /*
* unlink the first link to a key from a keyring * unlink the first link to a key from a keyring
......
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