KEYS: Expand the capacity of a keyring

Expand the capacity of a keyring to be able to hold a lot more keys by using
the previously added associative array implementation.  Currently the maximum
capacity is:

	(PAGE_SIZE - sizeof(header)) / sizeof(struct key *)

which, on a 64-bit system, is a little more 500.  However, since this is being
used for the NFS uid mapper, we need more than that.  The new implementation
gives us effectively unlimited capacity.

With some alterations, the keyutils testsuite runs successfully to completion
after this patch is applied.  The alterations are because (a) keyrings that
are simply added to no longer appear ordered and (b) some of the errors have
changed a bit.

Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells 2013-09-24 10:35:18 +01:00
parent 3cb989501c
commit b2a4df200d
9 changed files with 802 additions and 761 deletions

View File

@ -1,6 +1,6 @@
/* Keyring key type
*
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
* Copyright (C) 2008, 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@ -13,19 +13,6 @@
#define _KEYS_KEYRING_TYPE_H
#include <linux/key.h>
#include <linux/rcupdate.h>
/*
* the keyring payload contains a list of the keys to which the keyring is
* subscribed
*/
struct keyring_list {
struct rcu_head rcu; /* RCU deletion hook */
unsigned short maxkeys; /* max keys this list can hold */
unsigned short nkeys; /* number of keys currently held */
unsigned short delkey; /* key to be unlinked by RCU */
struct key __rcu *keys[0];
};
#include <linux/assoc_array.h>
#endif /* _KEYS_KEYRING_TYPE_H */

View File

@ -22,6 +22,7 @@
#include <linux/sysctl.h>
#include <linux/rwsem.h>
#include <linux/atomic.h>
#include <linux/assoc_array.h>
#ifdef __KERNEL__
#include <linux/uidgid.h>
@ -196,11 +197,13 @@ struct key {
* whatever
*/
union {
unsigned long value;
void __rcu *rcudata;
void *data;
struct keyring_list __rcu *subscriptions;
} payload;
union {
unsigned long value;
void __rcu *rcudata;
void *data;
} payload;
struct assoc_array keys;
};
};
extern struct key *key_alloc(struct key_type *type,

View File

@ -12,6 +12,7 @@
*/
//#define DEBUG
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/assoc_array_priv.h>
/*

View File

@ -4,6 +4,7 @@
config KEYS
bool "Enable access key retention support"
select ASSOCIATIVE_ARRAY
help
This option provides support for retaining authentication tokens and
access keys in the kernel.

View File

@ -130,6 +130,13 @@ void key_gc_keytype(struct key_type *ktype)
kleave("");
}
static int key_gc_keyring_func(const void *object, void *iterator_data)
{
const struct key *key = object;
time_t *limit = iterator_data;
return key_is_dead(key, *limit);
}
/*
* Garbage collect pointers from a keyring.
*
@ -138,10 +145,9 @@ void key_gc_keytype(struct key_type *ktype)
*/
static void key_gc_keyring(struct key *keyring, time_t limit)
{
struct keyring_list *klist;
int loop;
int result;
kenter("%x", key_serial(keyring));
kenter("%x{%s}", keyring->serial, keyring->description ?: "");
if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED)))
@ -149,27 +155,17 @@ static void key_gc_keyring(struct key *keyring, time_t limit)
/* scan the keyring looking for dead keys */
rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
if (!klist)
goto unlock_dont_gc;
loop = klist->nkeys;
smp_rmb();
for (loop--; loop >= 0; loop--) {
struct key *key = rcu_dereference(klist->keys[loop]);
if (key_is_dead(key, limit))
goto do_gc;
}
unlock_dont_gc:
result = assoc_array_iterate(&keyring->keys,
key_gc_keyring_func, &limit);
rcu_read_unlock();
if (result == true)
goto do_gc;
dont_gc:
kleave(" [no gc]");
return;
do_gc:
rcu_read_unlock();
keyring_gc(keyring, limit);
kleave(" [gc]");
}
@ -392,7 +388,6 @@ found_unreferenced_key:
*/
found_keyring:
spin_unlock(&key_serial_lock);
kdebug("scan keyring %d", key->serial);
key_gc_keyring(key, limit);
goto maybe_resched;

View File

@ -90,20 +90,23 @@ extern void key_type_put(struct key_type *ktype);
extern int __key_link_begin(struct key *keyring,
const struct keyring_index_key *index_key,
unsigned long *_prealloc);
struct assoc_array_edit **_edit);
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
extern void __key_link(struct key *keyring, struct key *key,
unsigned long *_prealloc);
extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
extern void __key_link_end(struct key *keyring,
const struct keyring_index_key *index_key,
unsigned long prealloc);
struct assoc_array_edit *edit);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct keyring_index_key *index_key);
extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
const struct keyring_index_key *index_key);
extern struct key *keyring_search_instkey(struct key *keyring,
key_serial_t target_id);
extern int iterate_over_keyring(const struct key *keyring,
int (*func)(const struct key *key, void *data),
void *data);
typedef int (*key_match_func_t)(const struct key *, const void *);
struct keyring_search_context {
@ -119,6 +122,8 @@ struct keyring_search_context {
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */
int (*iterator)(const void *object, void *iterator_data);
/* Internal stuff */
int skipped_ret;
bool possessed;

View File

@ -409,7 +409,7 @@ static int __key_instantiate_and_link(struct key *key,
struct key_preparsed_payload *prep,
struct key *keyring,
struct key *authkey,
unsigned long *_prealloc)
struct assoc_array_edit **_edit)
{
int ret, awaken;
@ -436,7 +436,7 @@ static int __key_instantiate_and_link(struct key *key,
/* and link it into the destination keyring */
if (keyring)
__key_link(keyring, key, _prealloc);
__key_link(key, _edit);
/* disable the authorisation key */
if (authkey)
@ -476,7 +476,7 @@ int key_instantiate_and_link(struct key *key,
struct key *authkey)
{
struct key_preparsed_payload prep;
unsigned long prealloc;
struct assoc_array_edit *edit;
int ret;
memset(&prep, 0, sizeof(prep));
@ -490,16 +490,15 @@ int key_instantiate_and_link(struct key *key,
}
if (keyring) {
ret = __key_link_begin(keyring, &key->index_key, &prealloc);
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0)
goto error_free_preparse;
}
ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
&prealloc);
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
if (keyring)
__key_link_end(keyring, &key->index_key, prealloc);
__key_link_end(keyring, &key->index_key, edit);
error_free_preparse:
if (key->type->preparse)
@ -537,7 +536,7 @@ int key_reject_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
unsigned long prealloc;
struct assoc_array_edit *edit;
struct timespec now;
int ret, awaken, link_ret = 0;
@ -548,7 +547,7 @@ int key_reject_and_link(struct key *key,
ret = -EBUSY;
if (keyring)
link_ret = __key_link_begin(keyring, &key->index_key, &prealloc);
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
mutex_lock(&key_construction_mutex);
@ -570,7 +569,7 @@ int key_reject_and_link(struct key *key,
/* and link it into the destination keyring */
if (keyring && link_ret == 0)
__key_link(keyring, key, &prealloc);
__key_link(key, &edit);
/* disable the authorisation key */
if (authkey)
@ -580,7 +579,7 @@ int key_reject_and_link(struct key *key,
mutex_unlock(&key_construction_mutex);
if (keyring)
__key_link_end(keyring, &key->index_key, prealloc);
__key_link_end(keyring, &key->index_key, edit);
/* wake up anyone waiting for a key to be constructed */
if (awaken)
@ -783,8 +782,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
.description = description,
};
struct key_preparsed_payload prep;
struct assoc_array_edit *edit;
const struct cred *cred = current_cred();
unsigned long prealloc;
struct key *keyring, *key = NULL;
key_ref_t key_ref;
int ret;
@ -828,7 +827,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
index_key.desc_len = strlen(index_key.description);
ret = __key_link_begin(keyring, &index_key, &prealloc);
ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_free_prep;
@ -847,8 +846,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
* update that instead if possible
*/
if (index_key.type->update) {
key_ref = __keyring_search_one(keyring_ref, &index_key);
if (!IS_ERR(key_ref))
key_ref = find_key_to_update(keyring_ref, &index_key);
if (key_ref)
goto found_matching_key;
}
@ -874,7 +873,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
/* instantiate it and link it into the target keyring */
ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);
if (ret < 0) {
key_put(key);
key_ref = ERR_PTR(ret);
@ -884,7 +883,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_link_end:
__key_link_end(keyring, &index_key, prealloc);
__key_link_end(keyring, &index_key, edit);
error_free_prep:
if (index_key.type->preparse)
index_key.type->free_preparse(&prep);
@ -897,7 +896,7 @@ error:
/* 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
*/
__key_link_end(keyring, &index_key, prealloc);
__key_link_end(keyring, &index_key, edit);
key_ref = __key_update(key_ref, &prep);
goto error_free_prep;

File diff suppressed because it is too large Load Diff

View File

@ -351,7 +351,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
struct key_user *user,
struct key **_key)
{
unsigned long prealloc;
struct assoc_array_edit *edit;
struct key *key;
key_perm_t perm;
key_ref_t key_ref;
@ -380,7 +380,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) {
ret = __key_link_begin(dest_keyring, &ctx->index_key, &prealloc);
ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
if (ret < 0)
goto link_prealloc_failed;
}
@ -395,11 +395,11 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
goto key_already_present;
if (dest_keyring)
__key_link(dest_keyring, key, &prealloc);
__key_link(key, &edit);
mutex_unlock(&key_construction_mutex);
if (dest_keyring)
__key_link_end(dest_keyring, &ctx->index_key, prealloc);
__key_link_end(dest_keyring, &ctx->index_key, edit);
mutex_unlock(&user->cons_lock);
*_key = key;
kleave(" = 0 [%d]", key_serial(key));
@ -414,8 +414,8 @@ key_already_present:
if (dest_keyring) {
ret = __key_link_check_live_key(dest_keyring, key);
if (ret == 0)
__key_link(dest_keyring, key, &prealloc);
__key_link_end(dest_keyring, &ctx->index_key, prealloc);
__key_link(key, &edit);
__key_link_end(dest_keyring, &ctx->index_key, edit);
if (ret < 0)
goto link_check_failed;
}