KEYS: Do preallocation for __key_link()

Do preallocation for __key_link() so that the various callers in request_key.c
can deal with any errors from this source before attempting to construct a key.
This allows them to assume that the actual linkage step is guaranteed to be
successful.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
This commit is contained in:
David Howells 2010-04-30 14:32:39 +01:00 committed by James Morris
parent 043b4d40f5
commit f70e2e0619
4 changed files with 215 additions and 130 deletions

View File

@ -87,7 +87,16 @@ extern wait_queue_head_t request_key_conswq;
extern struct key_type *key_type_lookup(const char *type); extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype); extern void key_type_put(struct key_type *ktype);
extern int __key_link(struct key *keyring, struct key *key); extern int __key_link_begin(struct key *keyring,
const struct key_type *type,
const char *description,
struct keyring_list **_prealloc);
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
extern void __key_link(struct key *keyring, struct key *key,
struct keyring_list **_prealloc);
extern void __key_link_end(struct key *keyring,
struct key_type *type,
struct keyring_list *prealloc);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref, extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct key_type *type, const struct key_type *type,

View File

@ -398,7 +398,8 @@ static int __key_instantiate_and_link(struct key *key,
const void *data, const void *data,
size_t datalen, size_t datalen,
struct key *keyring, struct key *keyring,
struct key *authkey) struct key *authkey,
struct keyring_list **_prealloc)
{ {
int ret, awaken; int ret, awaken;
@ -425,7 +426,7 @@ static int __key_instantiate_and_link(struct key *key,
/* and link it into the destination keyring */ /* and link it into the destination keyring */
if (keyring) if (keyring)
ret = __key_link(keyring, key); __key_link(keyring, key, _prealloc);
/* disable the authorisation key */ /* disable the authorisation key */
if (authkey) if (authkey)
@ -453,15 +454,21 @@ int key_instantiate_and_link(struct key *key,
struct key *keyring, struct key *keyring,
struct key *authkey) struct key *authkey)
{ {
struct keyring_list *prealloc;
int ret; int ret;
if (keyring) if (keyring) {
down_write(&keyring->sem); ret = __key_link_begin(keyring, key->type, key->description,
&prealloc);
if (ret < 0)
return ret;
}
ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey); ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
&prealloc);
if (keyring) if (keyring)
up_write(&keyring->sem); __key_link_end(keyring, key->type, prealloc);
return ret; return ret;
@ -478,8 +485,9 @@ int key_negate_and_link(struct key *key,
struct key *keyring, struct key *keyring,
struct key *authkey) struct key *authkey)
{ {
struct keyring_list *prealloc;
struct timespec now; struct timespec now;
int ret, awaken; int ret, awaken, link_ret = 0;
key_check(key); key_check(key);
key_check(keyring); key_check(keyring);
@ -488,7 +496,8 @@ int key_negate_and_link(struct key *key,
ret = -EBUSY; ret = -EBUSY;
if (keyring) if (keyring)
down_write(&keyring->sem); link_ret = __key_link_begin(keyring, key->type,
key->description, &prealloc);
mutex_lock(&key_construction_mutex); mutex_lock(&key_construction_mutex);
@ -508,8 +517,8 @@ int key_negate_and_link(struct key *key,
ret = 0; ret = 0;
/* and link it into the destination keyring */ /* and link it into the destination keyring */
if (keyring) if (keyring && link_ret == 0)
ret = __key_link(keyring, key); __key_link(keyring, key, &prealloc);
/* disable the authorisation key */ /* disable the authorisation key */
if (authkey) if (authkey)
@ -519,13 +528,13 @@ int key_negate_and_link(struct key *key,
mutex_unlock(&key_construction_mutex); mutex_unlock(&key_construction_mutex);
if (keyring) if (keyring)
up_write(&keyring->sem); __key_link_end(keyring, key->type, prealloc);
/* wake up anyone waiting for a key to be constructed */ /* wake up anyone waiting for a key to be constructed */
if (awaken) if (awaken)
wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT); wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
return ret; return ret == 0 ? link_ret : ret;
} /* end key_negate_and_link() */ } /* end key_negate_and_link() */
@ -749,6 +758,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_perm_t perm, key_perm_t perm,
unsigned long flags) unsigned long flags)
{ {
struct keyring_list *prealloc;
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
struct key_type *ktype; struct key_type *ktype;
struct key *keyring, *key = NULL; struct key *keyring, *key = NULL;
@ -775,7 +785,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
if (keyring->type != &key_type_keyring) if (keyring->type != &key_type_keyring)
goto error_2; goto error_2;
down_write(&keyring->sem); ret = __key_link_begin(keyring, ktype, description, &prealloc);
if (ret < 0)
goto error_2;
/* if we're going to allocate a new key, we're going to have /* if we're going to allocate a new key, we're going to have
* to modify the keyring */ * to modify the keyring */
@ -817,7 +829,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
} }
/* instantiate it and link it into the target keyring */ /* instantiate it and link it into the target keyring */
ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL); ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
&prealloc);
if (ret < 0) { if (ret < 0) {
key_put(key); key_put(key);
key_ref = ERR_PTR(ret); key_ref = ERR_PTR(ret);
@ -827,7 +840,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_3: error_3:
up_write(&keyring->sem); __key_link_end(keyring, ktype, prealloc);
error_2: error_2:
key_type_put(ktype); key_type_put(ktype);
error: error:
@ -837,7 +850,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
/* we found a matching key, so we're going to try to update it /* we found a matching key, so we're going to try to update it
* - we can drop the locks first as we have the key pinned * - we can drop the locks first as we have the key pinned
*/ */
up_write(&keyring->sem); __key_link_end(keyring, ktype, prealloc);
key_type_put(ktype); key_type_put(ktype);
key_ref = __key_update(key_ref, payload, plen); key_ref = __key_update(key_ref, payload, plen);

View File

@ -660,20 +660,6 @@ cycle_detected:
} /* end keyring_detect_cycle() */ } /* end keyring_detect_cycle() */
/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period
*/
static void keyring_link_rcu_disposal(struct rcu_head *rcu)
{
struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu);
kfree(klist);
} /* end keyring_link_rcu_disposal() */
/*****************************************************************************/
/* /*
* dispose of a keyring list after the RCU grace period, freeing the unlinked * dispose of a keyring list after the RCU grace period, freeing the unlinked
* key * key
@ -683,56 +669,51 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
struct keyring_list *klist = struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu); container_of(rcu, struct keyring_list, rcu);
key_put(klist->keys[klist->delkey]); if (klist->delkey != USHORT_MAX)
key_put(klist->keys[klist->delkey]);
kfree(klist); kfree(klist);
}
} /* end keyring_unlink_rcu_disposal() */
/*****************************************************************************/
/* /*
* link a key into to a keyring * preallocate memory so that a key can be linked into to a keyring
* - 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_begin(struct key *keyring, const struct key_type *type,
const char *description,
struct keyring_list **_prealloc)
__acquires(&keyring->sem)
{ {
struct keyring_list *klist, *nklist; struct keyring_list *klist, *nklist;
unsigned max; unsigned max;
size_t size; size_t size;
int loop, ret; int loop, ret;
kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
if (keyring->type != &key_type_keyring)
return -ENOTDIR;
down_write(&keyring->sem);
ret = -EKEYREVOKED; ret = -EKEYREVOKED;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
goto error; goto error_krsem;
ret = -ENOTDIR; /* serialise link/link calls to prevent parallel calls causing a cycle
if (keyring->type != &key_type_keyring) * when linking two keyring in opposite orders */
goto error; if (type == &key_type_keyring)
/* do some special keyring->keyring link checks */
if (key->type == &key_type_keyring) {
/* serialise link/link calls to prevent parallel calls causing
* a cycle when applied to two keyring in opposite orders */
down_write(&keyring_serialise_link_sem); down_write(&keyring_serialise_link_sem);
/* check that we aren't going to create a cycle adding one klist = rcu_dereference_locked_keyring(keyring);
* keyring to another */
ret = keyring_detect_cycle(keyring, key);
if (ret < 0)
goto error2;
}
/* see if there's a matching key we can displace */ /* see if there's a matching key we can displace */
klist = rcu_dereference_locked_keyring(keyring);
if (klist && klist->nkeys > 0) { if (klist && klist->nkeys > 0) {
struct key_type *type = key->type;
for (loop = klist->nkeys - 1; loop >= 0; loop--) { for (loop = klist->nkeys - 1; loop >= 0; loop--) {
if (klist->keys[loop]->type == type && if (klist->keys[loop]->type == type &&
strcmp(klist->keys[loop]->description, strcmp(klist->keys[loop]->description,
key->description) == 0 description) == 0
) { ) {
/* found a match - replace with new key */ /* found a match - we'll replace this one with
* the new key */
size = sizeof(struct key *) * klist->maxkeys; size = sizeof(struct key *) * klist->maxkeys;
size += sizeof(*klist); size += sizeof(*klist);
BUG_ON(size > PAGE_SIZE); BUG_ON(size > PAGE_SIZE);
@ -740,22 +721,10 @@ int __key_link(struct key *keyring, struct key *key)
ret = -ENOMEM; ret = -ENOMEM;
nklist = kmemdup(klist, size, GFP_KERNEL); nklist = kmemdup(klist, size, GFP_KERNEL);
if (!nklist) if (!nklist)
goto error2; goto error_sem;
/* 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);
/* note replacement slot */
klist->delkey = nklist->delkey = loop;
goto done; goto done;
} }
} }
@ -765,16 +734,11 @@ int __key_link(struct key *keyring, struct key *key)
ret = key_payload_reserve(keyring, ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES); keyring->datalen + KEYQUOTA_LINK_BYTES);
if (ret < 0) if (ret < 0)
goto error2; goto error_sem;
if (klist && klist->nkeys < klist->maxkeys) { if (klist && klist->nkeys < klist->maxkeys) {
/* there's sufficient slack space to add directly */ /* there's sufficient slack space to append directly */
atomic_inc(&key->usage); nklist = NULL;
klist->keys[klist->nkeys] = key;
smp_wmb();
klist->nkeys++;
smp_wmb();
} else { } else {
/* grow the key list */ /* grow the key list */
max = 4; max = 4;
@ -782,71 +746,155 @@ int __key_link(struct key *keyring, struct key *key)
max += klist->maxkeys; max += klist->maxkeys;
ret = -ENFILE; ret = -ENFILE;
if (max > 65535) if (max > USHORT_MAX - 1)
goto error3; goto error_quota;
size = sizeof(*klist) + sizeof(struct key *) * max; size = sizeof(*klist) + sizeof(struct key *) * max;
if (size > PAGE_SIZE) if (size > PAGE_SIZE)
goto error3; goto error_quota;
ret = -ENOMEM; ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL); nklist = kmalloc(size, GFP_KERNEL);
if (!nklist) if (!nklist)
goto error3; goto error_quota;
nklist->maxkeys = max;
nklist->nkeys = 0;
nklist->maxkeys = max;
if (klist) { if (klist) {
nklist->nkeys = klist->nkeys; memcpy(nklist->keys, klist->keys,
memcpy(nklist->keys,
klist->keys,
sizeof(struct key *) * klist->nkeys); sizeof(struct key *) * klist->nkeys);
nklist->delkey = klist->nkeys;
nklist->nkeys = klist->nkeys + 1;
klist->delkey = USHORT_MAX;
} else {
nklist->nkeys = 1;
nklist->delkey = 0;
} }
/* add the key into the new space */ /* add the key into the new space */
atomic_inc(&key->usage); nklist->keys[nklist->delkey] = NULL;
nklist->keys[nklist->nkeys++] = key;
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
/* dispose of the old keyring list */
if (klist)
call_rcu(&klist->rcu, keyring_link_rcu_disposal);
} }
done: done:
ret = 0; *_prealloc = nklist;
error2: kleave(" = 0");
if (key->type == &key_type_keyring) return 0;
up_write(&keyring_serialise_link_sem);
error:
return ret;
error3: error_quota:
/* 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);
goto error2; error_sem:
if (type == &key_type_keyring)
up_write(&keyring_serialise_link_sem);
error_krsem:
up_write(&keyring->sem);
kleave(" = %d", ret);
return ret;
}
} /* end __key_link() */ /*
* check already instantiated keys aren't going to be a problem
* - the caller must have called __key_link_begin()
* - don't need to call this for keys that were created since __key_link_begin()
* was called
*/
int __key_link_check_live_key(struct key *keyring, struct key *key)
{
if (key->type == &key_type_keyring)
/* check that we aren't going to create a cycle by linking one
* keyring to another */
return keyring_detect_cycle(keyring, key);
return 0;
}
/*
* link a key into to a keyring
* - must be called with __key_link_begin() having being called
* - discard already extant link to matching key if there is one
*/
void __key_link(struct key *keyring, struct key *key,
struct keyring_list **_prealloc)
{
struct keyring_list *klist, *nklist;
nklist = *_prealloc;
*_prealloc = NULL;
kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
klist = rcu_dereference_protected(keyring->payload.subscriptions,
rwsem_is_locked(&keyring->sem));
atomic_inc(&key->usage);
/* there's a matching key we can displace or an empty slot in a newly
* allocated list we can fill */
if (nklist) {
kdebug("replace %hu/%hu/%hu",
nklist->delkey, nklist->nkeys, nklist->maxkeys);
nklist->keys[nklist->delkey] = key;
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
/* dispose of the old keyring list and, if there was one, the
* displaced key */
if (klist) {
kdebug("dispose %hu/%hu/%hu",
klist->delkey, klist->nkeys, klist->maxkeys);
call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
}
} else {
/* there's sufficient slack space to append directly */
klist->keys[klist->nkeys] = key;
smp_wmb();
klist->nkeys++;
}
}
/*
* finish linking a key into to a keyring
* - must be called with __key_link_begin() having being called
*/
void __key_link_end(struct key *keyring, struct key_type *type,
struct keyring_list *prealloc)
__releases(&keyring->sem)
{
BUG_ON(type == NULL);
BUG_ON(type->name == NULL);
kenter("%d,%s,%p", keyring->serial, type->name, prealloc);
if (type == &key_type_keyring)
up_write(&keyring_serialise_link_sem);
if (prealloc) {
kfree(prealloc);
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
}
up_write(&keyring->sem);
}
/*****************************************************************************/
/* /*
* link a key to a keyring * link a key to a keyring
*/ */
int key_link(struct key *keyring, struct key *key) int key_link(struct key *keyring, struct key *key)
{ {
struct keyring_list *prealloc;
int ret; int ret;
key_check(keyring); key_check(keyring);
key_check(key); key_check(key);
down_write(&keyring->sem); ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
ret = __key_link(keyring, key); if (ret == 0) {
up_write(&keyring->sem); ret = __key_link_check_live_key(keyring, key);
if (ret == 0)
__key_link(keyring, key, &prealloc);
__key_link_end(keyring, key->type, prealloc);
}
return ret; return ret;
}
} /* end key_link() */
EXPORT_SYMBOL(key_link); EXPORT_SYMBOL(key_link);

View File

@ -299,6 +299,7 @@ static int construct_alloc_key(struct key_type *type,
struct key_user *user, struct key_user *user,
struct key **_key) struct key **_key)
{ {
struct keyring_list *prealloc;
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
struct key *key; struct key *key;
key_ref_t key_ref; key_ref_t key_ref;
@ -306,6 +307,7 @@ static int construct_alloc_key(struct key_type *type,
kenter("%s,%s,,,", type->name, description); kenter("%s,%s,,,", type->name, description);
*_key = NULL;
mutex_lock(&user->cons_lock); mutex_lock(&user->cons_lock);
key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred, key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
@ -315,8 +317,12 @@ static int construct_alloc_key(struct key_type *type,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) if (dest_keyring) {
down_write(&dest_keyring->sem); ret = __key_link_begin(dest_keyring, type, description,
&prealloc);
if (ret < 0)
goto link_prealloc_failed;
}
/* attach the key to the destination keyring under lock, but we do need /* attach the key to the destination keyring under lock, but we do need
* to do another check just in case someone beat us to it whilst we * to do another check just in case someone beat us to it whilst we
@ -328,11 +334,11 @@ static int construct_alloc_key(struct key_type *type,
goto key_already_present; goto key_already_present;
if (dest_keyring) if (dest_keyring)
__key_link(dest_keyring, key); __key_link(dest_keyring, key, &prealloc);
mutex_unlock(&key_construction_mutex); mutex_unlock(&key_construction_mutex);
if (dest_keyring) if (dest_keyring)
up_write(&dest_keyring->sem); __key_link_end(dest_keyring, type, prealloc);
mutex_unlock(&user->cons_lock); mutex_unlock(&user->cons_lock);
*_key = key; *_key = key;
kleave(" = 0 [%d]", key_serial(key)); kleave(" = 0 [%d]", key_serial(key));
@ -341,27 +347,36 @@ static int construct_alloc_key(struct key_type *type,
/* the key is now present - we tell the caller that we found it by /* the key is now present - we tell the caller that we found it by
* returning -EINPROGRESS */ * returning -EINPROGRESS */
key_already_present: key_already_present:
key_put(key);
mutex_unlock(&key_construction_mutex); mutex_unlock(&key_construction_mutex);
ret = 0; key = key_ref_to_ptr(key_ref);
if (dest_keyring) { if (dest_keyring) {
ret = __key_link(dest_keyring, key_ref_to_ptr(key_ref)); ret = __key_link_check_live_key(dest_keyring, key);
up_write(&dest_keyring->sem); if (ret == 0)
__key_link(dest_keyring, key, &prealloc);
__key_link_end(dest_keyring, type, prealloc);
if (ret < 0)
goto link_check_failed;
} }
mutex_unlock(&user->cons_lock); mutex_unlock(&user->cons_lock);
key_put(key); *_key = key;
if (ret < 0) {
key_ref_put(key_ref);
*_key = NULL;
kleave(" = %d [link]", ret);
return ret;
}
*_key = key = key_ref_to_ptr(key_ref);
kleave(" = -EINPROGRESS [%d]", key_serial(key)); kleave(" = -EINPROGRESS [%d]", key_serial(key));
return -EINPROGRESS; return -EINPROGRESS;
link_check_failed:
mutex_unlock(&user->cons_lock);
key_put(key);
kleave(" = %d [linkcheck]", ret);
return ret;
link_prealloc_failed:
up_write(&dest_keyring->sem);
mutex_unlock(&user->cons_lock);
kleave(" = %d [prelink]", ret);
return ret;
alloc_failed: alloc_failed:
mutex_unlock(&user->cons_lock); mutex_unlock(&user->cons_lock);
*_key = NULL;
kleave(" = %ld", PTR_ERR(key)); kleave(" = %ld", PTR_ERR(key));
return PTR_ERR(key); return PTR_ERR(key);
} }