Commit b2a4df20 authored by David Howells's avatar David Howells

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: default avatarDavid Howells <dhowells@redhat.com>
parent 3cb98950
/* Keyring key type /* 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) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
...@@ -13,19 +13,6 @@ ...@@ -13,19 +13,6 @@
#define _KEYS_KEYRING_TYPE_H #define _KEYS_KEYRING_TYPE_H
#include <linux/key.h> #include <linux/key.h>
#include <linux/rcupdate.h> #include <linux/assoc_array.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];
};
#endif /* _KEYS_KEYRING_TYPE_H */ #endif /* _KEYS_KEYRING_TYPE_H */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/assoc_array.h>
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/uidgid.h> #include <linux/uidgid.h>
...@@ -195,12 +196,14 @@ struct key { ...@@ -195,12 +196,14 @@ struct key {
* - this is used to hold the data actually used in cryptography or * - this is used to hold the data actually used in cryptography or
* whatever * whatever
*/ */
union {
union { union {
unsigned long value; unsigned long value;
void __rcu *rcudata; void __rcu *rcudata;
void *data; void *data;
struct keyring_list __rcu *subscriptions;
} payload; } payload;
struct assoc_array keys;
};
}; };
extern struct key *key_alloc(struct key_type *type, extern struct key *key_alloc(struct key_type *type,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
*/ */
//#define DEBUG //#define DEBUG
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h>
#include <linux/assoc_array_priv.h> #include <linux/assoc_array_priv.h>
/* /*
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
config KEYS config KEYS
bool "Enable access key retention support" bool "Enable access key retention support"
select ASSOCIATIVE_ARRAY
help help
This option provides support for retaining authentication tokens and This option provides support for retaining authentication tokens and
access keys in the kernel. access keys in the kernel.
......
...@@ -130,6 +130,13 @@ void key_gc_keytype(struct key_type *ktype) ...@@ -130,6 +130,13 @@ void key_gc_keytype(struct key_type *ktype)
kleave(""); 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. * Garbage collect pointers from a keyring.
* *
...@@ -138,10 +145,9 @@ void key_gc_keytype(struct key_type *ktype) ...@@ -138,10 +145,9 @@ void key_gc_keytype(struct key_type *ktype)
*/ */
static void key_gc_keyring(struct key *keyring, time_t limit) static void key_gc_keyring(struct key *keyring, time_t limit)
{ {
struct keyring_list *klist; int result;
int loop;
kenter("%x", key_serial(keyring)); kenter("%x{%s}", keyring->serial, keyring->description ?: "");
if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED))) (1 << KEY_FLAG_REVOKED)))
...@@ -149,27 +155,17 @@ static void key_gc_keyring(struct key *keyring, time_t limit) ...@@ -149,27 +155,17 @@ static void key_gc_keyring(struct key *keyring, time_t limit)
/* scan the keyring looking for dead keys */ /* scan the keyring looking for dead keys */
rcu_read_lock(); rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions); result = assoc_array_iterate(&keyring->keys,
if (!klist) key_gc_keyring_func, &limit);
goto unlock_dont_gc; rcu_read_unlock();
if (result == true)
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; goto do_gc;
}
unlock_dont_gc:
rcu_read_unlock();
dont_gc: dont_gc:
kleave(" [no gc]"); kleave(" [no gc]");
return; return;
do_gc: do_gc:
rcu_read_unlock();
keyring_gc(keyring, limit); keyring_gc(keyring, limit);
kleave(" [gc]"); kleave(" [gc]");
} }
...@@ -392,7 +388,6 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -392,7 +388,6 @@ static void key_garbage_collector(struct work_struct *work)
*/ */
found_keyring: found_keyring:
spin_unlock(&key_serial_lock); spin_unlock(&key_serial_lock);
kdebug("scan keyring %d", key->serial);
key_gc_keyring(key, limit); key_gc_keyring(key, limit);
goto maybe_resched; goto maybe_resched;
......
...@@ -90,20 +90,23 @@ extern void key_type_put(struct key_type *ktype); ...@@ -90,20 +90,23 @@ extern void key_type_put(struct key_type *ktype);
extern int __key_link_begin(struct key *keyring, extern int __key_link_begin(struct key *keyring,
const struct keyring_index_key *index_key, 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 int __key_link_check_live_key(struct key *keyring, struct key *key);
extern void __key_link(struct key *keyring, struct key *key, extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
unsigned long *_prealloc);
extern void __key_link_end(struct key *keyring, extern void __key_link_end(struct key *keyring,
const struct keyring_index_key *index_key, 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, extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
const struct keyring_index_key *index_key); const struct keyring_index_key *index_key);
extern struct key *keyring_search_instkey(struct key *keyring, extern struct key *keyring_search_instkey(struct key *keyring,
key_serial_t target_id); 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 *); typedef int (*key_match_func_t)(const struct key *, const void *);
struct keyring_search_context { struct keyring_search_context {
...@@ -119,6 +122,8 @@ 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_NO_CHECK_PERM 0x0010 /* Don't check permissions */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */ #define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */
int (*iterator)(const void *object, void *iterator_data);
/* Internal stuff */ /* Internal stuff */
int skipped_ret; int skipped_ret;
bool possessed; bool possessed;
......
...@@ -409,7 +409,7 @@ static int __key_instantiate_and_link(struct key *key, ...@@ -409,7 +409,7 @@ static int __key_instantiate_and_link(struct key *key,
struct key_preparsed_payload *prep, struct key_preparsed_payload *prep,
struct key *keyring, struct key *keyring,
struct key *authkey, struct key *authkey,
unsigned long *_prealloc) struct assoc_array_edit **_edit)
{ {
int ret, awaken; int ret, awaken;
...@@ -436,7 +436,7 @@ static int __key_instantiate_and_link(struct key *key, ...@@ -436,7 +436,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)
__key_link(keyring, key, _prealloc); __key_link(key, _edit);
/* disable the authorisation key */ /* disable the authorisation key */
if (authkey) if (authkey)
...@@ -476,7 +476,7 @@ int key_instantiate_and_link(struct key *key, ...@@ -476,7 +476,7 @@ int key_instantiate_and_link(struct key *key,
struct key *authkey) struct key *authkey)
{ {
struct key_preparsed_payload prep; struct key_preparsed_payload prep;
unsigned long prealloc; struct assoc_array_edit *edit;
int ret; int ret;
memset(&prep, 0, sizeof(prep)); memset(&prep, 0, sizeof(prep));
...@@ -490,16 +490,15 @@ int key_instantiate_and_link(struct key *key, ...@@ -490,16 +490,15 @@ int key_instantiate_and_link(struct key *key,
} }
if (keyring) { if (keyring) {
ret = __key_link_begin(keyring, &key->index_key, &prealloc); ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0) if (ret < 0)
goto error_free_preparse; goto error_free_preparse;
} }
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
&prealloc);
if (keyring) if (keyring)
__key_link_end(keyring, &key->index_key, prealloc); __key_link_end(keyring, &key->index_key, edit);
error_free_preparse: error_free_preparse:
if (key->type->preparse) if (key->type->preparse)
...@@ -537,7 +536,7 @@ int key_reject_and_link(struct key *key, ...@@ -537,7 +536,7 @@ int key_reject_and_link(struct key *key,
struct key *keyring, struct key *keyring,
struct key *authkey) struct key *authkey)
{ {
unsigned long prealloc; struct assoc_array_edit *edit;
struct timespec now; struct timespec now;
int ret, awaken, link_ret = 0; int ret, awaken, link_ret = 0;
...@@ -548,7 +547,7 @@ int key_reject_and_link(struct key *key, ...@@ -548,7 +547,7 @@ int key_reject_and_link(struct key *key,
ret = -EBUSY; ret = -EBUSY;
if (keyring) 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); mutex_lock(&key_construction_mutex);
...@@ -570,7 +569,7 @@ int key_reject_and_link(struct key *key, ...@@ -570,7 +569,7 @@ int key_reject_and_link(struct key *key,
/* and link it into the destination keyring */ /* and link it into the destination keyring */
if (keyring && link_ret == 0) if (keyring && link_ret == 0)
__key_link(keyring, key, &prealloc); __key_link(key, &edit);
/* disable the authorisation key */ /* disable the authorisation key */
if (authkey) if (authkey)
...@@ -580,7 +579,7 @@ int key_reject_and_link(struct key *key, ...@@ -580,7 +579,7 @@ int key_reject_and_link(struct key *key,
mutex_unlock(&key_construction_mutex); mutex_unlock(&key_construction_mutex);
if (keyring) 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 */ /* wake up anyone waiting for a key to be constructed */
if (awaken) if (awaken)
...@@ -783,8 +782,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -783,8 +782,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
.description = description, .description = description,
}; };
struct key_preparsed_payload prep; struct key_preparsed_payload prep;
struct assoc_array_edit *edit;
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
unsigned long prealloc;
struct key *keyring, *key = NULL; struct key *keyring, *key = NULL;
key_ref_t key_ref; key_ref_t key_ref;
int ret; int ret;
...@@ -828,7 +827,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -828,7 +827,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
} }
index_key.desc_len = strlen(index_key.description); 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) { if (ret < 0) {
key_ref = ERR_PTR(ret); key_ref = ERR_PTR(ret);
goto error_free_prep; goto error_free_prep;
...@@ -847,8 +846,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -847,8 +846,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
* update that instead if possible * update that instead if possible
*/ */
if (index_key.type->update) { if (index_key.type->update) {
key_ref = __keyring_search_one(keyring_ref, &index_key); key_ref = find_key_to_update(keyring_ref, &index_key);
if (!IS_ERR(key_ref)) if (key_ref)
goto found_matching_key; goto found_matching_key;
} }
...@@ -874,7 +873,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -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 */ /* 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) { if (ret < 0) {
key_put(key); key_put(key);
key_ref = ERR_PTR(ret); key_ref = ERR_PTR(ret);
...@@ -884,7 +883,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -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)); key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_link_end: error_link_end:
__key_link_end(keyring, &index_key, prealloc); __key_link_end(keyring, &index_key, edit);
error_free_prep: error_free_prep:
if (index_key.type->preparse) if (index_key.type->preparse)
index_key.type->free_preparse(&prep); index_key.type->free_preparse(&prep);
...@@ -897,7 +896,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -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 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
*/ */
__key_link_end(keyring, &index_key, prealloc); __key_link_end(keyring, &index_key, edit);
key_ref = __key_update(key_ref, &prep); key_ref = __key_update(key_ref, &prep);
goto error_free_prep; goto error_free_prep;
......
This diff is collapsed.
...@@ -351,7 +351,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, ...@@ -351,7 +351,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
struct key_user *user, struct key_user *user,
struct key **_key) struct key **_key)
{ {
unsigned long prealloc; struct assoc_array_edit *edit;
struct key *key; struct key *key;
key_perm_t perm; key_perm_t perm;
key_ref_t key_ref; key_ref_t key_ref;
...@@ -380,7 +380,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, ...@@ -380,7 +380,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) { 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) if (ret < 0)
goto link_prealloc_failed; goto link_prealloc_failed;
} }
...@@ -395,11 +395,11 @@ static int construct_alloc_key(struct keyring_search_context *ctx, ...@@ -395,11 +395,11 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
goto key_already_present; goto key_already_present;
if (dest_keyring) if (dest_keyring)
__key_link(dest_keyring, key, &prealloc); __key_link(key, &edit);
mutex_unlock(&key_construction_mutex); mutex_unlock(&key_construction_mutex);
if (dest_keyring) 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); mutex_unlock(&user->cons_lock);
*_key = key; *_key = key;
kleave(" = 0 [%d]", key_serial(key)); kleave(" = 0 [%d]", key_serial(key));
...@@ -414,8 +414,8 @@ static int construct_alloc_key(struct keyring_search_context *ctx, ...@@ -414,8 +414,8 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
if (dest_keyring) { if (dest_keyring) {
ret = __key_link_check_live_key(dest_keyring, key); ret = __key_link_check_live_key(dest_keyring, key);
if (ret == 0) if (ret == 0)
__key_link(dest_keyring, key, &prealloc); __key_link(key, &edit);
__key_link_end(dest_keyring, &ctx->index_key, prealloc); __key_link_end(dest_keyring, &ctx->index_key, edit);
if (ret < 0) if (ret < 0)
goto link_check_failed; goto link_check_failed;
} }
......
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