Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
KEYS: Expand the capacity of a keyring
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dhowells committed Sep 24, 2013
1 parent 3cb9895 commit b2a4df2
Show file tree
Hide file tree
Showing 9 changed files with 803 additions and 762 deletions.
17 changes: 2 additions & 15 deletions include/keys/keyring-type.h
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 */
13 changes: 8 additions & 5 deletions include/linux/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions lib/assoc_array.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
//#define DEBUG
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/assoc_array_priv.h>

/*
Expand Down
1 change: 1 addition & 0 deletions security/keys/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
33 changes: 14 additions & 19 deletions security/keys/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -138,38 +145,27 @@ 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)))
goto dont_gc;

/* 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]");
}
Expand Down Expand Up @@ -392,7 +388,6 @@ static void key_garbage_collector(struct work_struct *work)
*/
found_keyring:
spin_unlock(&key_serial_lock);
kdebug("scan keyring %d", key->serial);
key_gc_keyring(key, limit);
goto maybe_resched;

Expand Down
17 changes: 11 additions & 6 deletions security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
35 changes: 17 additions & 18 deletions security/keys/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -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));
Expand All @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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);

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -897,7 +896,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 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;
Expand Down
Loading

0 comments on commit b2a4df2

Please sign in to comment.