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>
...@@ -196,11 +197,13 @@ struct key { ...@@ -196,11 +197,13 @@ struct key {
* whatever * whatever
*/ */
union { union {
unsigned long value; union {
void __rcu *rcudata; unsigned long value;
void *data; void __rcu *rcudata;
struct keyring_list __rcu *subscriptions; void *data;
} 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;
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:
rcu_read_unlock(); rcu_read_unlock();
if (result == true)
goto do_gc;
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;
......
/* Keyring handling /* Keyring handling
* *
* Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2004-2005, 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
...@@ -17,25 +17,11 @@ ...@@ -17,25 +17,11 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/err.h> #include <linux/err.h>
#include <keys/keyring-type.h> #include <keys/keyring-type.h>
#include <keys/user-type.h>
#include <linux/assoc_array_priv.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "internal.h" #include "internal.h"
#define rcu_dereference_locked_keyring(keyring) \
(rcu_dereference_protected( \
(keyring)->payload.subscriptions, \
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
#define rcu_deref_link_locked(klist, index, keyring) \
(rcu_dereference_protected( \
(klist)->keys[index], \
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
#define MAX_KEYRING_LINKS \
min_t(size_t, USHRT_MAX - 1, \
((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *)))
#define KEY_LINK_FIXQUOTA 1UL
/* /*
* When plumbing the depths of the key tree, this sets a hard limit * When plumbing the depths of the key tree, this sets a hard limit
* set on how deep we're willing to go. * set on how deep we're willing to go.
...@@ -47,6 +33,28 @@ ...@@ -47,6 +33,28 @@
*/ */
#define KEYRING_NAME_HASH_SIZE (1 << 5) #define KEYRING_NAME_HASH_SIZE (1 << 5)
/*
* We mark pointers we pass to the associative array with bit 1 set if
* they're keyrings and clear otherwise.
*/
#define KEYRING_PTR_SUBTYPE 0x2UL
static inline bool keyring_ptr_is_keyring(const struct assoc_array_ptr *x)
{
return (unsigned long)x & KEYRING_PTR_SUBTYPE;
}
static inline struct key *keyring_ptr_to_key(const struct assoc_array_ptr *x)
{
void *object = assoc_array_ptr_to_leaf(x);
return (struct key *)((unsigned long)object & ~KEYRING_PTR_SUBTYPE);
}
static inline void *keyring_key_to_ptr(struct key *key)
{
if (key->type == &key_type_keyring)
return (void *)((unsigned long)key | KEYRING_PTR_SUBTYPE);
return key;
}
static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE]; static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
static DEFINE_RWLOCK(keyring_name_lock); static DEFINE_RWLOCK(keyring_name_lock);
...@@ -67,7 +75,6 @@ static inline unsigned keyring_hash(const char *desc) ...@@ -67,7 +75,6 @@ static inline unsigned keyring_hash(const char *desc)
*/ */
static int keyring_instantiate(struct key *keyring, static int keyring_instantiate(struct key *keyring,
struct key_preparsed_payload *prep); struct key_preparsed_payload *prep);
static int keyring_match(const struct key *keyring, const void *criterion);
static void keyring_revoke(struct key *keyring); static void keyring_revoke(struct key *keyring);
static void keyring_destroy(struct key *keyring); static void keyring_destroy(struct key *keyring);
static void keyring_describe(const struct key *keyring, struct seq_file *m); static void keyring_describe(const struct key *keyring, struct seq_file *m);
...@@ -76,9 +83,9 @@ static long keyring_read(const struct key *keyring, ...@@ -76,9 +83,9 @@ static long keyring_read(const struct key *keyring,
struct key_type key_type_keyring = { struct key_type key_type_keyring = {
.name = "keyring", .name = "keyring",
.def_datalen = sizeof(struct keyring_list), .def_datalen = 0,
.instantiate = keyring_instantiate, .instantiate = keyring_instantiate,
.match = keyring_match, .match = user_match,
.revoke = keyring_revoke, .revoke = keyring_revoke,
.destroy = keyring_destroy, .destroy = keyring_destroy,
.describe = keyring_describe, .describe = keyring_describe,
...@@ -127,6 +134,7 @@ static int keyring_instantiate(struct key *keyring, ...@@ -127,6 +134,7 @@ static int keyring_instantiate(struct key *keyring,
ret = -EINVAL; ret = -EINVAL;
if (prep->datalen == 0) { if (prep->datalen == 0) {
assoc_array_init(&keyring->keys);
/* make the keyring available by name if it has one */ /* make the keyring available by name if it has one */
keyring_publish_name(keyring); keyring_publish_name(keyring);
ret = 0; ret = 0;
...@@ -136,14 +144,225 @@ static int keyring_instantiate(struct key *keyring, ...@@ -136,14 +144,225 @@ static int keyring_instantiate(struct key *keyring,
} }
/* /*
* Match keyrings on their name * Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit. Ideally we'd
* fold the carry back too, but that requires inline asm.
*/
static u64 mult_64x32_and_fold(u64 x, u32 y)
{
u64 hi = (u64)(u32)(x >> 32) * y;
u64 lo = (u64)(u32)(x) * y;
return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32);
}
/*
* Hash a key type and description.
*/
static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
{
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
const unsigned long level_mask = ASSOC_ARRAY_LEVEL_STEP_MASK;
const char *description = index_key->description;
unsigned long hash, type;
u32 piece;
u64 acc;
int n, desc_len = index_key->desc_len;
type = (unsigned long)index_key->type;
acc = mult_64x32_and_fold(type, desc_len + 13);
acc = mult_64x32_and_fold(acc, 9207);
for (;;) {
n = desc_len;
if (n <= 0)
break;
if (n > 4)
n = 4;
piece = 0;
memcpy(&piece, description, n);
description += n;
desc_len -= n;
acc = mult_64x32_and_fold(acc, piece);
acc = mult_64x32_and_fold(acc, 9207);
}
/* Fold the hash down to 32 bits if need be. */
hash = acc;
if (ASSOC_ARRAY_KEY_CHUNK_SIZE == 32)
hash ^= acc >> 32;
/* Squidge all the keyrings into a separate part of the tree to
* ordinary keys by making sure the lowest level segment in the hash is
* zero for keyrings and non-zero otherwise.
*/
if (index_key->type != &key_type_keyring && (hash & level_mask) == 0)
return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
if (index_key->type == &key_type_keyring && (hash & level_mask) != 0)
return (hash + (hash << level_shift)) & ~level_mask;
return hash;
}
/*
* Build the next index key chunk.
*
* On 32-bit systems the index key is laid out as:
*
* 0 4 5 9...
* hash desclen typeptr desc[]
*
* On 64-bit systems:
*
* 0 8 9 17...
* hash desclen typeptr desc[]
*
* We return it one word-sized chunk at a time.
*/ */
static int keyring_match(const struct key *keyring, const void *description) static unsigned long keyring_get_key_chunk(const void *data, int level)
{
const struct keyring_index_key *index_key = data;
unsigned long chunk = 0;
long offset = 0;
int desc_len = index_key->desc_len, n = sizeof(chunk);
level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
switch (level) {
case 0:
return hash_key_type_and_desc(index_key);
case 1:
return ((unsigned long)index_key->type << 8) | desc_len;
case 2:
if (desc_len == 0)
return (u8)((unsigned long)index_key->type >>
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
n--;
offset = 1;
default:
offset += sizeof(chunk) - 1;
offset += (level - 3) * sizeof(chunk);
if (offset >= desc_len)
return 0;
desc_len -= offset;
if (desc_len > n)
desc_len = n;
offset += desc_len;
do {
chunk <<= 8;
chunk |= ((u8*)index_key->description)[--offset];
} while (--desc_len > 0);
if (level == 2) {
chunk <<= 8;
chunk |= (u8)((unsigned long)index_key->type >>
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
}
return chunk;
}
}
static unsigned long keyring_get_object_key_chunk(const void *object, int level)
{
const struct key *key = keyring_ptr_to_key(object);
return keyring_get_key_chunk(&key->index_key, level);
}
static bool keyring_compare_object(const void *object, const void *data)
{ {
return keyring->description && const struct keyring_index_key *index_key = data;
strcmp(keyring->description, description) == 0; const struct key *key = keyring_ptr_to_key(object);
return key->index_key.type == index_key->type &&
key->index_key.desc_len == index_key->desc_len &&
memcmp(key->index_key.description, index_key->description,
index_key->desc_len) == 0;
} }
/*
* Compare the index keys of a pair of objects and determine the bit position
* at which they differ - if they differ.
*/
static int keyring_diff_objects(const void *_a, const void *_b)
{
const struct key *key_a = keyring_ptr_to_key(_a);
const struct key *key_b = keyring_ptr_to_key(_b);
const struct keyring_index_key *a = &key_a->index_key;
const struct keyring_index_key *b = &key_b->index_key;
unsigned long seg_a, seg_b;
int level, i;
level = 0;
seg_a = hash_key_type_and_desc(a);
seg_b = hash_key_type_and_desc(b);
if ((seg_a ^ seg_b) != 0)
goto differ;
/* The number of bits contributed by the hash is controlled by a
* constant in the assoc_array headers. Everything else thereafter we
* can deal with as being machine word-size dependent.
*/
level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
seg_a = a->desc_len;
seg_b = b->desc_len;
if ((seg_a ^ seg_b) != 0)
goto differ;
/* The next bit may not work on big endian */
level++;
seg_a = (unsigned long)a->type;
seg_b = (unsigned long)b->type;
if ((seg_a ^ seg_b) != 0)
goto differ;
level += sizeof(unsigned long);
if (a->desc_len == 0)
goto same;
i = 0;
if (((unsigned long)a->description | (unsigned long)b->description) &
(sizeof(unsigned long) - 1)) {
do {
seg_a = *(unsigned long *)(a->description + i);
seg_b = *(unsigned long *)(b->description + i);
if ((seg_a ^ seg_b) != 0)
goto differ_plus_i;
i += sizeof(unsigned long);
} while (i < (a->desc_len & (sizeof(unsigned long) - 1)));
}
for (; i < a->desc_len; i++) {
seg_a = *(unsigned char *)(a->description + i);
seg_b = *(unsigned char *)(b->description + i);
if ((seg_a ^ seg_b) != 0)
goto differ_plus_i;
}
same:
return -1;
differ_plus_i:
level += i;
differ:
i = level * 8 + __ffs(seg_a ^ seg_b);
return i;
}
/*
* Free an object after stripping the keyring flag off of the pointer.
*/
static void keyring_free_object(void *object)
{
key_put(keyring_ptr_to_key(object));
}
/*
* Operations for keyring management by the index-tree routines.
*/
static const struct assoc_array_ops keyring_assoc_array_ops = {
.get_key_chunk = keyring_get_key_chunk,
.get_object_key_chunk = keyring_get_object_key_chunk,
.compare_object = keyring_compare_object,
.diff_objects = keyring_diff_objects,
.free_object = keyring_free_object,
};
/* /*
* Clean up a keyring when it is destroyed. Unpublish its name if it had one * Clean up a keyring when it is destroyed. Unpublish its name if it had one
* and dispose of its data. * and dispose of its data.
...@@ -155,9 +374,6 @@ static int keyring_match(const struct key *keyring, const void *description) ...@@ -155,9 +374,6 @@ static int keyring_match(const struct key *keyring, const void *description)
*/ */
static void keyring_destroy(struct key *keyring) static void keyring_destroy(struct key *keyring)
{ {
struct keyring_list *klist;
int loop;
if (keyring->description) { if (keyring->description) {
write_lock(&keyring_name_lock); write_lock(&keyring_name_lock);
...@@ -168,12 +384,7 @@ static void keyring_destroy(struct key *keyring) ...@@ -168,12 +384,7 @@ static void keyring_destroy(struct key *keyring)
write_unlock(&keyring_name_lock); write_unlock(&keyring_name_lock);
} }
klist = rcu_access_pointer(keyring->payload.subscriptions); assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
if (klist) {
for (loop = klist->nkeys - 1; loop >= 0; loop--)
key_put(rcu_access_pointer(klist->keys[loop]));
kfree(klist);
}
} }
/* /*
...@@ -181,76 +392,88 @@ static void keyring_destroy(struct key *keyring) ...@@ -181,76 +392,88 @@ static void keyring_destroy(struct key *keyring)
*/ */
static void keyring_describe(const struct key *keyring, struct seq_file *m) static void keyring_describe(const struct key *keyring, struct seq_file *m)
{ {
struct keyring_list *klist;
if (keyring->description) if (keyring->description)
seq_puts(m, keyring->description); seq_puts(m, keyring->description);
else else
seq_puts(m, "[anon]"); seq_puts(m, "[anon]");
if (key_is_instantiated(keyring)) { if (key_is_instantiated(keyring)) {
rcu_read_lock(); if (keyring->keys.nr_leaves_on_tree != 0)
klist = rcu_dereference(keyring->payload.subscriptions); seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
if (klist)
seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
else else
seq_puts(m, ": empty"); seq_puts(m, ": empty");
rcu_read_unlock();
} }
} }
struct keyring_read_iterator_context {
size_t qty;
size_t count;
key_serial_t __user *buffer;
};
static int keyring_read_iterator(const void *object, void *data)
{
struct keyring_read_iterator_context *ctx = data;
const struct key *key = keyring_ptr_to_key(object);
int ret;
kenter("{%s,%d},,{%zu/%zu}",
key->type->name, key->serial, ctx->count, ctx->qty);
if (ctx->count >= ctx->qty)
return 1;
ret = put_user(key->serial, ctx->buffer);
if (ret < 0)
return ret;
ctx->buffer++;
ctx->count += sizeof(key->serial);
return 0;
}
/* /*
* Read a list of key IDs from the keyring's contents in binary form * Read a list of key IDs from the keyring's contents in binary form
* *
* The keyring's semaphore is read-locked by the caller. * The keyring's semaphore is read-locked by the caller. This prevents someone
* from modifying it under us - which could cause us to read key IDs multiple
* times.
*/ */
static long keyring_read(const struct key *keyring, static long keyring_read(const struct key *keyring,
char __user *buffer, size_t buflen) char __user *buffer, size_t buflen)
{ {
struct keyring_list *klist; struct keyring_read_iterator_context ctx;
struct key *key; unsigned long nr_keys;
size_t qty, tmp; int ret;
int loop, ret;
ret = 0; kenter("{%d},,%zu", key_serial(keyring), buflen);
klist = rcu_dereference_locked_keyring(keyring);
if (klist) { if (buflen & (sizeof(key_serial_t) - 1))
/* calculate how much data we could return */ return -EINVAL;
qty = klist->nkeys * sizeof(key_serial_t);
nr_keys = keyring->keys.nr_leaves_on_tree;
if (buffer && buflen > 0) { if (nr_keys == 0)
if (buflen > qty) return 0;
buflen = qty;
/* copy the IDs of the subscribed keys into the
* buffer */
ret = -EFAULT;
for (loop = 0; loop < klist->nkeys; loop++) {
key = rcu_deref_link_locked(klist, loop,
keyring);
tmp = sizeof(key_serial_t);
if (tmp > buflen)
tmp = buflen;
if (copy_to_user(buffer,
&key->serial,
tmp) != 0)
goto error;
buflen -= tmp;
if (buflen == 0)
break;
buffer += tmp;
}
}
ret = qty; /* Calculate how much data we could return */
ctx.qty = nr_keys * sizeof(key_serial_t);
if (!buffer || !buflen)
return ctx.qty;
if (buflen > ctx.qty)
ctx.qty = buflen;
/* Copy the IDs of the subscribed keys into the buffer */
ctx.buffer = (key_serial_t __user *)buffer;
ctx.count = 0;
ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx);
if (ret < 0) {
kleave(" = %d [iterate]", ret);
return ret;
} }
error: kleave(" = %zu [ok]", ctx.count);
return ret; return ctx.count;
} }
/* /*
...@@ -277,219 +500,360 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, ...@@ -277,219 +500,360 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
} }
EXPORT_SYMBOL(keyring_alloc); EXPORT_SYMBOL(keyring_alloc);
/** /*
* keyring_search_aux - Search a keyring tree for a key matching some criteria * Iteration function to consider each key found.
* @keyring_ref: A pointer to the keyring with possession indicator.
* @ctx: The keyring search context.
*
* Search the supplied keyring tree for a key that matches the criteria given.
* The root keyring and any linked keyrings must grant Search permission to the
* caller to be searchable and keys can only be found if they too grant Search
* to the caller. The possession flag on the root keyring pointer controls use
* of the possessor bits in permissions checking of the entire tree. In
* addition, the LSM gets to forbid keyring searches and key matches.
*
* The search is performed as a breadth-then-depth search up to the prescribed
* limit (KEYRING_SEARCH_MAX_DEPTH).
*
* Keys are matched to the type provided and are then filtered by the match
* function, which is given the description to use in any way it sees fit. The
* match function may use any attributes of a key that it wishes to to
* determine the match. Normally the match function from the key type would be
* used.
*
* RCU is used to prevent the keyring key lists from disappearing without the
* need to take lots of locks.
*
* Returns a pointer to the found key and increments the key usage count if
* successful; -EAGAIN if no matching keys were found, or if expired or revoked
* keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the
* specified keyring wasn't a keyring.
*
* In the case of a successful return, the possession attribute from
* @keyring_ref is propagated to the returned key reference.
*/ */
key_ref_t keyring_search_aux(key_ref_t keyring_ref, static int keyring_search_iterator(const void *object, void *iterator_data)
struct keyring_search_context *ctx)
{ {
struct { struct keyring_search_context *ctx = iterator_data;
/* Need a separate keylist pointer for RCU purposes */ const struct key *key = keyring_ptr_to_key(object);
struct key *keyring; unsigned long kflags = key->flags;
struct keyring_list *keylist;
int kix;
} stack[KEYRING_SEARCH_MAX_DEPTH];
struct keyring_list *keylist;
unsigned long kflags;
struct key *keyring, *key;
key_ref_t key_ref;
long err;
int sp, nkeys, kix;
keyring = key_ref_to_ptr(keyring_ref); kenter("{%d}", key->serial);
ctx->possessed = is_key_possessed(keyring_ref);
key_check(keyring);
/* top keyring must have search permission to begin the search */ /* ignore keys not of this type */
err = key_task_permission(keyring_ref, ctx->cred, KEY_SEARCH); if (key->type != ctx->index_key.type) {
if (err < 0) { kleave(" = 0 [!type]");
key_ref = ERR_PTR(err); return 0;
goto error;
} }
key_ref = ERR_PTR(-ENOTDIR); /* skip invalidated, revoked and expired keys */
if (keyring->type != &key_type_keyring) if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
goto error; if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED))) {
ctx->result = ERR_PTR(-EKEYREVOKED);
kleave(" = %d [invrev]", ctx->skipped_ret);
goto skipped;
}
rcu_read_lock(); if (key->expiry && ctx->now.tv_sec >= key->expiry) {
ctx->result = ERR_PTR(-EKEYEXPIRED);
kleave(" = %d [expire]", ctx->skipped_ret);
goto skipped;
}
}
ctx->now = current_kernel_time(); /* keys that don't match */
err = -EAGAIN; if (!ctx->match(key, ctx->match_data)) {
sp = 0; kleave(" = 0 [!match]");
return 0;
/* firstly we should check to see if this top-level keyring is what we }
* are looking for */
key_ref = ERR_PTR(-EAGAIN);
kflags = keyring->flags;
if (keyring->type == ctx->index_key.type &&
ctx->match(keyring, ctx->match_data)) {
key = keyring;
if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
goto found;
/* check it isn't negative and hasn't expired or been /* key must have search permissions */
* revoked */ if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) &&
if (kflags & (1 << KEY_FLAG_REVOKED)) key_task_permission(make_key_ref(key, ctx->possessed),
goto error_2; ctx->cred, KEY_SEARCH) < 0) {
if (key->expiry && ctx->now.tv_sec >= key->expiry) ctx->result = ERR_PTR(-EACCES);
goto error_2; kleave(" = %d [!perm]", ctx->skipped_ret);
key_ref = ERR_PTR(key->type_data.reject_error); goto skipped;
if (kflags & (1 << KEY_FLAG_NEGATIVE))
goto error_2;
goto found;
} }
/* otherwise, the top keyring must not be revoked, expired, or if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
* negatively instantiated if we are to search it */ /* we set a different error code if we pass a negative key */
key_ref = ERR_PTR(-EAGAIN); if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
if (kflags & ((1 << KEY_FLAG_INVALIDATED) | ctx->result = ERR_PTR(key->type_data.reject_error);
(1 << KEY_FLAG_REVOKED) | kleave(" = %d [neg]", ctx->skipped_ret);
(1 << KEY_FLAG_NEGATIVE)) || goto skipped;
(keyring->expiry && ctx->now.tv_sec >= keyring->expiry)) }
goto error_2; }
/* start processing a new keyring */
descend:
kflags = keyring->flags;
if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED)))
goto not_this_keyring;
keylist = rcu_dereference(keyring->payload.subscriptions); /* Found */
if (!keylist) ctx->result = make_key_ref(key, ctx->possessed);
goto not_this_keyring; kleave(" = 1 [found]");
return 1;
/* iterate through the keys in this keyring first */ skipped:
nkeys = keylist->nkeys; return ctx->skipped_ret;
smp_rmb(); }
for (kix = 0; kix < nkeys; kix++) {
key = rcu_dereference(keylist->keys[kix]);
kflags = key->flags;
/* ignore keys not of this type */ /*
if (key->type != ctx->index_key.type) * Search inside a keyring for a key. We can search by walking to it
continue; * directly based on its index-key or we can iterate over the entire
* tree looking for it, based on the match function.
*/
static int search_keyring(struct key *keyring, struct keyring_search_context *ctx)
{
if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) ==
KEYRING_SEARCH_LOOKUP_DIRECT) {
const void *object;
object = assoc_array_find(&keyring->keys,
&keyring_assoc_array_ops,
&ctx->index_key);
return object ? ctx->iterator(object, ctx) : 0;
}
return assoc_array_iterate(&keyring->keys, ctx->iterator, ctx);
}
/* skip invalidated, revoked and expired keys */ /*
if (!(ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)) { * Search a tree of keyrings that point to other keyrings up to the maximum
if (kflags & ((1 << KEY_FLAG_INVALIDATED) | * depth.
(1 << KEY_FLAG_REVOKED))) */
continue; static bool search_nested_keyrings(struct key *keyring,
struct keyring_search_context *ctx)
{
struct {
struct key *keyring;
struct assoc_array_node *node;
int slot;
} stack[KEYRING_SEARCH_MAX_DEPTH];
if (key->expiry && ctx->now.tv_sec >= key->expiry) struct assoc_array_shortcut *shortcut;
continue; struct assoc_array_node *node;
} struct assoc_array_ptr *ptr;
struct key *key;
int sp = 0, slot;
/* keys that don't match */ kenter("{%d},{%s,%s}",
if (!ctx->match(key, ctx->match_data)) keyring->serial,
continue; ctx->index_key.type->name,
ctx->index_key.description);
/* key must have search permissions */ if (ctx->index_key.description)
if (key_task_permission(make_key_ref(key, ctx->possessed), ctx->index_key.desc_len = strlen(ctx->index_key.description);
ctx->cred, KEY_SEARCH) < 0)
continue;
if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK) /* Check to see if this top-level keyring is what we are looking for
* and whether it is valid or not.
*/
if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE ||
keyring_compare_object(keyring, &ctx->index_key)) {
ctx->skipped_ret = 2;
ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) {
case 1:
goto found; goto found;
case 2:
/* we set a different error code if we pass a negative key */ return false;
if (kflags & (1 << KEY_FLAG_NEGATIVE)) { default:
err = key->type_data.reject_error; break;
continue;
} }
}
ctx->skipped_ret = 0;
if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK;
/* Start processing a new keyring */
descend_to_keyring:
kdebug("descend to %d", keyring->serial);
if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED)))
goto not_this_keyring;
/* Search through the keys in this keyring before its searching its
* subtrees.
*/
if (search_keyring(keyring, ctx))
goto found; goto found;
}
/* search through the keyrings nested in this one */ /* Then manually iterate through the keyrings nested in this one.
kix = 0; *
ascend: * Start from the root node of the index tree. Because of the way the
nkeys = keylist->nkeys; * hash function has been set up, keyrings cluster on the leftmost
smp_rmb(); * branch of the root node (root slot 0) or in the root node itself.
for (; kix < nkeys; kix++) { * Non-keyrings avoid the leftmost branch of the root entirely (root
key = rcu_dereference(keylist->keys[kix]); * slots 1-15).
if (key->type != &key_type_keyring) */
continue; ptr = ACCESS_ONCE(keyring->keys.root);
if (!ptr)
goto not_this_keyring;
/* recursively search nested keyrings if (assoc_array_ptr_is_shortcut(ptr)) {
* - only search keyrings for which we have search permission /* If the root is a shortcut, either the keyring only contains
* keyring pointers (everything clusters behind root slot 0) or
* doesn't contain any keyring pointers.
*/ */
if (sp >= KEYRING_SEARCH_MAX_DEPTH) shortcut = assoc_array_ptr_to_shortcut(ptr);
smp_read_barrier_depends();
if ((shortcut->index_key[0] & ASSOC_ARRAY_FAN_MASK) != 0)
goto not_this_keyring;
ptr = ACCESS_ONCE(shortcut->next_node);
node = assoc_array_ptr_to_node(ptr);
goto begin_node;
}
node = assoc_array_ptr_to_node(ptr);
smp_read_barrier_depends();
ptr = node->slots[0];
if (!assoc_array_ptr_is_meta(ptr))
goto begin_node;
descend_to_node:
/* Descend to a more distal node in this keyring's content tree and go
* through that.
*/
kdebug("descend");
if (assoc_array_ptr_is_shortcut(ptr)) {
shortcut = assoc_array_ptr_to_shortcut(ptr);
smp_read_barrier_depends();
ptr = ACCESS_ONCE(shortcut->next_node);
BUG_ON(!assoc_array_ptr_is_node(ptr));
node = assoc_array_ptr_to_node(ptr);
}
begin_node:
kdebug("begin_node");
smp_read_barrier_depends();
slot = 0;
ascend_to_node:
/* Go through the slots in a node */
for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = ACCESS_ONCE(node->slots[slot]);
if (assoc_array_ptr_is_meta(ptr) && node->back_pointer)
goto descend_to_node;
if (!keyring_ptr_is_keyring(ptr))
continue; continue;
if (key_task_permission(make_key_ref(key, ctx->possessed), key = keyring_ptr_to_key(ptr);
if (sp >= KEYRING_SEARCH_MAX_DEPTH) {
if (ctx->flags & KEYRING_SEARCH_DETECT_TOO_DEEP) {
ctx->result = ERR_PTR(-ELOOP);
return false;
}
goto not_this_keyring;
}
/* Search a nested keyring */
if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) &&
key_task_permission(make_key_ref(key, ctx->possessed),
ctx->cred, KEY_SEARCH) < 0) ctx->cred, KEY_SEARCH) < 0)
continue; continue;
/* stack the current position */ /* stack the current position */
stack[sp].keyring = keyring; stack[sp].keyring = keyring;
stack[sp].keylist = keylist; stack[sp].node = node;
stack[sp].kix = kix; stack[sp].slot = slot;
sp++; sp++;
/* begin again with the new keyring */ /* begin again with the new keyring */
keyring = key; keyring = key;
goto descend; goto descend_to_keyring;
}
/* We've dealt with all the slots in the current node, so now we need
* to ascend to the parent and continue processing there.
*/
ptr = ACCESS_ONCE(node->back_pointer);
slot = node->parent_slot;
if (ptr && assoc_array_ptr_is_shortcut(ptr)) {
shortcut = assoc_array_ptr_to_shortcut(ptr);
smp_read_barrier_depends();
ptr = ACCESS_ONCE(shortcut->back_pointer);
slot = shortcut->parent_slot;
}
if (!ptr)
goto not_this_keyring;
node = assoc_array_ptr_to_node(ptr);
smp_read_barrier_depends();
slot++;
/* If we've ascended to the root (zero backpointer), we must have just
* finished processing the leftmost branch rather than the root slots -
* so there can't be any more keyrings for us to find.
*/
if (node->back_pointer) {
kdebug("ascend %d", slot);
goto ascend_to_node;
} }
/* the keyring we're looking at was disqualified or didn't contain a /* The keyring we're looking at was disqualified or didn't contain a
* matching key */ * matching key.
*/
not_this_keyring: not_this_keyring:
if (sp > 0) { kdebug("not_this_keyring %d", sp);
/* resume the processing of a keyring higher up in the tree */ if (sp <= 0) {
sp--; kleave(" = false");
keyring = stack[sp].keyring; return false;
keylist = stack[sp].keylist;
kix = stack[sp].kix + 1;
goto ascend;
} }
key_ref = ERR_PTR(err); /* Resume the processing of a keyring higher up in the tree */
goto error_2; sp--;
keyring = stack[sp].keyring;
node = stack[sp].node;
slot = stack[sp].slot + 1;
kdebug("ascend to %d [%d]", keyring->serial, slot);
goto ascend_to_node;
/* we found a viable match */ /* We found a viable match */
found: found:
__key_get(key); key = key_ref_to_ptr(ctx->result);
key->last_used_at = ctx->now.tv_sec;
keyring->last_used_at = ctx->now.tv_sec;
while (sp > 0)
stack[--sp].keyring->last_used_at = ctx->now.tv_sec;
key_check(key); key_check(key);
key_ref = make_key_ref(key, ctx->possessed); if (!(ctx->flags & KEYRING_SEARCH_NO_UPDATE_TIME)) {
error_2: key->last_used_at = ctx->now.tv_sec;
keyring->last_used_at = ctx->now.tv_sec;
while (sp > 0)
stack[--sp].keyring->last_used_at = ctx->now.tv_sec;
}
kleave(" = true");
return true;
}
/**
* keyring_search_aux - Search a keyring tree for a key matching some criteria
* @keyring_ref: A pointer to the keyring with possession indicator.
* @ctx: The keyring search context.
*
* Search the supplied keyring tree for a key that matches the criteria given.
* The root keyring and any linked keyrings must grant Search permission to the
* caller to be searchable and keys can only be found if they too grant Search
* to the caller. The possession flag on the root keyring pointer controls use
* of the possessor bits in permissions checking of the entire tree. In
* addition, the LSM gets to forbid keyring searches and key matches.
*
* The search is performed as a breadth-then-depth search up to the prescribed
* limit (KEYRING_SEARCH_MAX_DEPTH).
*
* Keys are matched to the type provided and are then filtered by the match
* function, which is given the description to use in any way it sees fit. The
* match function may use any attributes of a key that it wishes to to
* determine the match. Normally the match function from the key type would be
* used.
*
* RCU can be used to prevent the keyring key lists from disappearing without
* the need to take lots of locks.
*
* Returns a pointer to the found key and increments the key usage count if
* successful; -EAGAIN if no matching keys were found, or if expired or revoked
* keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the
* specified keyring wasn't a keyring.
*
* In the case of a successful return, the possession attribute from
* @keyring_ref is propagated to the returned key reference.
*/
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
struct keyring_search_context *ctx)
{
struct key *keyring;
long err;
ctx->iterator = keyring_search_iterator;
ctx->possessed = is_key_possessed(keyring_ref);
ctx->result = ERR_PTR(-EAGAIN);
keyring = key_ref_to_ptr(keyring_ref);
key_check(keyring);
if (keyring->type != &key_type_keyring)
return ERR_PTR(-ENOTDIR);
if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM)) {
err = key_task_permission(keyring_ref, ctx->cred, KEY_SEARCH);
if (err < 0)
return ERR_PTR(err);
}
rcu_read_lock();
ctx->now = current_kernel_time();
if (search_nested_keyrings(keyring, ctx))
__key_get(key_ref_to_ptr(ctx->result));
rcu_read_unlock(); rcu_read_unlock();
error: return ctx->result;
return key_ref;
} }
/** /**
...@@ -499,7 +863,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, ...@@ -499,7 +863,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
* @description: The name of the keyring we want to find. * @description: The name of the keyring we want to find.
* *
* As keyring_search_aux() above, but using the current task's credentials and * As keyring_search_aux() above, but using the current task's credentials and
* type's default matching function. * type's default matching function and preferred search method.
*/ */
key_ref_t keyring_search(key_ref_t keyring, key_ref_t keyring_search(key_ref_t keyring,
struct key_type *type, struct key_type *type,
...@@ -523,58 +887,49 @@ key_ref_t keyring_search(key_ref_t keyring, ...@@ -523,58 +887,49 @@ key_ref_t keyring_search(key_ref_t keyring,
EXPORT_SYMBOL(keyring_search); EXPORT_SYMBOL(keyring_search);
/* /*
* Search the given keyring only (no recursion). * Search the given keyring for a key that might be updated.
* *
* The caller must guarantee that the keyring is a keyring and that the * The caller must guarantee that the keyring is a keyring and that the
* permission is granted to search the keyring as no check is made here. * permission is granted to modify the keyring as no check is made here. The
* * caller must also hold a lock on the keyring semaphore.
* RCU is used to make it unnecessary to lock the keyring key list here.
* *
* Returns a pointer to the found key with usage count incremented if * Returns a pointer to the found key with usage count incremented if
* successful and returns -ENOKEY if not found. Revoked and invalidated keys * successful and returns NULL if not found. Revoked and invalidated keys are
* are skipped over. * skipped over.
* *
* If successful, the possession indicator is propagated from the keyring ref * If successful, the possession indicator is propagated from the keyring ref
* to the returned key reference. * to the returned key reference.
*/ */
key_ref_t __keyring_search_one(key_ref_t keyring_ref, 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)
{ {
struct keyring_list *klist;
struct key *keyring, *key; struct key *keyring, *key;
bool possessed; const void *object;
int nkeys, loop;
keyring = key_ref_to_ptr(keyring_ref); keyring = key_ref_to_ptr(keyring_ref);
possessed = is_key_possessed(keyring_ref);
rcu_read_lock(); kenter("{%d},{%s,%s}",
keyring->serial, index_key->type->name, index_key->description);
klist = rcu_dereference(keyring->payload.subscriptions); object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops,
if (klist) { index_key);
nkeys = klist->nkeys;
smp_rmb();
for (loop = 0; loop < nkeys ; loop++) {
key = rcu_dereference(klist->keys[loop]);
if (key->type == index_key->type &&
(!key->type->match ||
key->type->match(key, index_key->description)) &&
!(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED)))
)
goto found;
}
}
rcu_read_unlock(); if (object)
return ERR_PTR(-ENOKEY); goto found;
kleave(" = NULL");
return NULL;
found: found:
key = keyring_ptr_to_key(object);
if (key->flags & ((1 << KEY_FLAG_INVALIDATED) |
(1 << KEY_FLAG_REVOKED))) {
kleave(" = NULL [x]");
return NULL;
}
__key_get(key); __key_get(key);
keyring->last_used_at = key->last_used_at = kleave(" = {%d}", key->serial);
current_kernel_time().tv_sec; return make_key_ref(key, is_key_possessed(keyring_ref));
rcu_read_unlock();
return make_key_ref(key, possessed);
} }
/* /*
...@@ -637,6 +992,19 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) ...@@ -637,6 +992,19 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
return keyring; return keyring;
} }
static int keyring_detect_cycle_iterator(const void *object,
void *iterator_data)
{
struct keyring_search_context *ctx = iterator_data;
const struct key *key = keyring_ptr_to_key(object);
kenter("{%d}", key->serial);
BUG_ON(key != ctx->match_data);
ctx->result = ERR_PTR(-EDEADLK);
return 1;
}
/* /*
* See if a cycle will will be created by inserting acyclic tree B in acyclic * See if a cycle will will be created by inserting acyclic tree B in acyclic
* tree A at the topmost level (ie: as a direct child of A). * tree A at the topmost level (ie: as a direct child of A).
...@@ -646,117 +1014,39 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) ...@@ -646,117 +1014,39 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
*/ */
static int keyring_detect_cycle(struct key *A, struct key *B) static int keyring_detect_cycle(struct key *A, struct key *B)
{ {
struct { struct keyring_search_context ctx = {
struct keyring_list *keylist; .index_key = A->index_key,
int kix; .match_data = A,
} stack[KEYRING_SEARCH_MAX_DEPTH]; .iterator = keyring_detect_cycle_iterator,
.flags = (KEYRING_SEARCH_LOOKUP_DIRECT |
struct keyring_list *keylist; KEYRING_SEARCH_NO_STATE_CHECK |
struct key *subtree, *key; KEYRING_SEARCH_NO_UPDATE_TIME |
int sp, nkeys, kix, ret; KEYRING_SEARCH_NO_CHECK_PERM |
KEYRING_SEARCH_DETECT_TOO_DEEP),
};
rcu_read_lock(); rcu_read_lock();
search_nested_keyrings(B, &ctx);
ret = -EDEADLK;
if (A == B)
goto cycle_detected;
subtree = B;
sp = 0;
/* start processing a new keyring */
descend:
if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
goto not_this_keyring;
keylist = rcu_dereference(subtree->payload.subscriptions);
if (!keylist)
goto not_this_keyring;
kix = 0;
ascend:
/* iterate through the remaining keys in this keyring */
nkeys = keylist->nkeys;
smp_rmb();
for (; kix < nkeys; kix++) {
key = rcu_dereference(keylist->keys[kix]);
if (key == A)
goto cycle_detected;
/* recursively check nested keyrings */
if (key->type == &key_type_keyring) {
if (sp >= KEYRING_SEARCH_MAX_DEPTH)
goto too_deep;
/* stack the current position */
stack[sp].keylist = keylist;
stack[sp].kix = kix;
sp++;
/* begin again with the new keyring */
subtree = key;
goto descend;
}
}
/* the keyring we're looking at was disqualified or didn't contain a
* matching key */
not_this_keyring:
if (sp > 0) {
/* resume the checking of a keyring higher up in the tree */
sp--;
keylist = stack[sp].keylist;
kix = stack[sp].kix + 1;
goto ascend;
}
ret = 0; /* no cycles detected */
error:
rcu_read_unlock(); rcu_read_unlock();
return ret; return PTR_ERR(ctx.result) == -EAGAIN ? 0 : PTR_ERR(ctx.result);
too_deep:
ret = -ELOOP;
goto error;
cycle_detected:
ret = -EDEADLK;
goto error;
}
/*
* 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);
if (klist->delkey != USHRT_MAX)
key_put(rcu_access_pointer(klist->keys[klist->delkey]));
kfree(klist);
} }
/* /*
* Preallocate memory so that a key can be linked into to a keyring. * Preallocate memory so that a key can be linked into to a keyring.
*/ */
int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key, int __key_link_begin(struct key *keyring,
unsigned long *_prealloc) const struct keyring_index_key *index_key,
struct assoc_array_edit **_edit)
__acquires(&keyring->sem) __acquires(&keyring->sem)
__acquires(&keyring_serialise_link_sem) __acquires(&keyring_serialise_link_sem)
{ {
struct keyring_list *klist, *nklist; struct assoc_array_edit *edit;
unsigned long prealloc; int ret;
unsigned max;
time_t lowest_lru;
size_t size;
int loop, lru, ret;
kenter("%d,%s,%s,", kenter("%d,%s,%s,",
key_serial(keyring), index_key->type->name, index_key->description); keyring->serial, index_key->type->name, index_key->description);
BUG_ON(index_key->desc_len == 0);
if (keyring->type != &key_type_keyring) if (keyring->type != &key_type_keyring)
return -ENOTDIR; return -ENOTDIR;
...@@ -772,88 +1062,25 @@ int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_ ...@@ -772,88 +1062,25 @@ int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_
if (index_key->type == &key_type_keyring) if (index_key->type == &key_type_keyring)
down_write(&keyring_serialise_link_sem); down_write(&keyring_serialise_link_sem);
klist = rcu_dereference_locked_keyring(keyring);
/* see if there's a matching key we can displace */
lru = -1;
if (klist && klist->nkeys > 0) {
lowest_lru = TIME_T_MAX;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
struct key *key = rcu_deref_link_locked(klist, loop,
keyring);
if (key->type == index_key->type &&
strcmp(key->description, index_key->description) == 0) {
/* Found a match - we'll replace the link with
* one to the new key. We record the slot
* position.
*/
klist->delkey = loop;
prealloc = 0;
goto done;
}
if (key->last_used_at < lowest_lru) {
lowest_lru = key->last_used_at;
lru = loop;
}
}
}
/* If the keyring is full then do an LRU discard */
if (klist &&
klist->nkeys == klist->maxkeys &&
klist->maxkeys >= MAX_KEYRING_LINKS) {
kdebug("LRU discard %d\n", lru);
klist->delkey = lru;
prealloc = 0;
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);
if (ret < 0) if (ret < 0)
goto error_sem; goto error_sem;
if (klist && klist->nkeys < klist->maxkeys) { /* Create an edit script that will insert/replace the key in the
/* there's sufficient slack space to append directly */ * keyring tree.
klist->delkey = klist->nkeys; */
prealloc = KEY_LINK_FIXQUOTA; edit = assoc_array_insert(&keyring->keys,
} else { &keyring_assoc_array_ops,
/* grow the key list */ index_key,
max = 4; NULL);
if (klist) { if (IS_ERR(edit)) {
max += klist->maxkeys; ret = PTR_ERR(edit);
if (max > MAX_KEYRING_LINKS) goto error_quota;
max = MAX_KEYRING_LINKS;
BUG_ON(max <= klist->maxkeys);
}
size = sizeof(*klist) + sizeof(struct key *) * max;
ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL);
if (!nklist)
goto error_quota;
nklist->maxkeys = max;
if (klist) {
memcpy(nklist->keys, klist->keys,
sizeof(struct key *) * klist->nkeys);
nklist->delkey = klist->nkeys;
nklist->nkeys = klist->nkeys + 1;
klist->delkey = USHRT_MAX;
} else {
nklist->nkeys = 1;
nklist->delkey = 0;
}
/* add the key into the new space */
RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL);
prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
} }
done: *_edit = edit;
*_prealloc = prealloc;
kleave(" = 0"); kleave(" = 0");
return 0; return 0;
...@@ -893,60 +1120,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key) ...@@ -893,60 +1120,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)
* holds at most one link to any given key of a particular type+description * holds at most one link to any given key of a particular type+description
* combination. * combination.
*/ */
void __key_link(struct key *keyring, struct key *key, void __key_link(struct key *key, struct assoc_array_edit **_edit)
unsigned long *_prealloc)
{ {
struct keyring_list *klist, *nklist;
struct key *discard;
nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
*_prealloc = 0;
kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
klist = rcu_dereference_locked_keyring(keyring);
__key_get(key); __key_get(key);
keyring->last_used_at = key->last_used_at = assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key));
current_kernel_time().tv_sec; assoc_array_apply_edit(*_edit);
*_edit = NULL;
/* there's a matching key we can displace or an empty slot in a newly
* allocated list we can fill */
if (nklist) {
kdebug("reissue %hu/%hu/%hu",
nklist->delkey, nklist->nkeys, nklist->maxkeys);
RCU_INIT_POINTER(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 if (klist->delkey < klist->nkeys) {
kdebug("replace %hu/%hu/%hu",
klist->delkey, klist->nkeys, klist->maxkeys);
discard = rcu_dereference_protected(
klist->keys[klist->delkey],
rwsem_is_locked(&keyring->sem));
rcu_assign_pointer(klist->keys[klist->delkey], key);
/* The garbage collector will take care of RCU
* synchronisation */
key_put(discard);
} else {
/* there's sufficient slack space to append directly */
kdebug("append %hu/%hu/%hu",
klist->delkey, klist->nkeys, klist->maxkeys);
RCU_INIT_POINTER(klist->keys[klist->delkey], key);
smp_wmb();
klist->nkeys++;
}
} }
/* /*
...@@ -956,23 +1135,20 @@ void __key_link(struct key *keyring, struct key *key, ...@@ -956,23 +1135,20 @@ void __key_link(struct key *keyring, struct key *key,
*/ */
void __key_link_end(struct key *keyring, 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)
__releases(&keyring->sem) __releases(&keyring->sem)
__releases(&keyring_serialise_link_sem) __releases(&keyring_serialise_link_sem)
{ {
BUG_ON(index_key->type == NULL); BUG_ON(index_key->type == NULL);
BUG_ON(index_key->type->name == NULL); kenter("%d,%s,", keyring->serial, index_key->type->name);
kenter("%d,%s,%lx", keyring->serial, index_key->type->name, prealloc);
if (index_key->type == &key_type_keyring) if (index_key->type == &key_type_keyring)
up_write(&keyring_serialise_link_sem); up_write(&keyring_serialise_link_sem);
if (prealloc) { if (edit) {
if (prealloc & KEY_LINK_FIXQUOTA) key_payload_reserve(keyring,
key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);
keyring->datalen - assoc_array_cancel_edit(edit);
KEYQUOTA_LINK_BYTES);
kfree((struct keyring_list *)(prealloc & ~KEY_LINK_FIXQUOTA));
} }
up_write(&keyring->sem); up_write(&keyring->sem);
} }
...@@ -999,20 +1175,24 @@ void __key_link_end(struct key *keyring, ...@@ -999,20 +1175,24 @@ void __key_link_end(struct key *keyring,
*/ */
int key_link(struct key *keyring, struct key *key) int key_link(struct key *keyring, struct key *key)
{ {
unsigned long prealloc; struct assoc_array_edit *edit;
int ret; int ret;
kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage));
key_check(keyring); key_check(keyring);
key_check(key); key_check(key);
ret = __key_link_begin(keyring, &key->index_key, &prealloc); ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret == 0) { if (ret == 0) {
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
ret = __key_link_check_live_key(keyring, key); ret = __key_link_check_live_key(keyring, key);
if (ret == 0) if (ret == 0)
__key_link(keyring, key, &prealloc); __key_link(key, &edit);
__key_link_end(keyring, &key->index_key, prealloc); __key_link_end(keyring, &key->index_key, edit);
} }
kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));
return ret; return ret;
} }
EXPORT_SYMBOL(key_link); EXPORT_SYMBOL(key_link);
...@@ -1036,90 +1216,36 @@ EXPORT_SYMBOL(key_link); ...@@ -1036,90 +1216,36 @@ EXPORT_SYMBOL(key_link);
*/ */
int key_unlink(struct key *keyring, struct key *key) int key_unlink(struct key *keyring, struct key *key)
{ {
struct keyring_list *klist, *nklist; struct assoc_array_edit *edit;
int loop, ret; int ret;
key_check(keyring); key_check(keyring);
key_check(key); key_check(key);
ret = -ENOTDIR;
if (keyring->type != &key_type_keyring) if (keyring->type != &key_type_keyring)
goto error; return -ENOTDIR;
down_write(&keyring->sem); down_write(&keyring->sem);
klist = rcu_dereference_locked_keyring(keyring); edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops,
if (klist) { &key->index_key);
/* search the keyring for the key */ if (IS_ERR(edit)) {
for (loop = 0; loop < klist->nkeys; loop++) ret = PTR_ERR(edit);
if (rcu_access_pointer(klist->keys[loop]) == key) goto error;
goto key_is_present;
} }
up_write(&keyring->sem);
ret = -ENOENT; ret = -ENOENT;
goto error; if (edit == NULL)
goto error;
key_is_present:
/* we need to copy the key list for RCU purposes */
nklist = kmalloc(sizeof(*klist) +
sizeof(struct key *) * klist->maxkeys,
GFP_KERNEL);
if (!nklist)
goto nomem;
nklist->maxkeys = klist->maxkeys;
nklist->nkeys = klist->nkeys - 1;
if (loop > 0)
memcpy(&nklist->keys[0],
&klist->keys[0],
loop * sizeof(struct key *));
if (loop < nklist->nkeys)
memcpy(&nklist->keys[loop],
&klist->keys[loop + 1],
(nklist->nkeys - loop) * sizeof(struct key *));
/* adjust the user's quota */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
up_write(&keyring->sem);
/* schedule for later cleanup */
klist->delkey = loop;
call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
assoc_array_apply_edit(edit);
ret = 0; ret = 0;
error: error:
return ret;
nomem:
ret = -ENOMEM;
up_write(&keyring->sem); up_write(&keyring->sem);
goto error; return ret;
} }
EXPORT_SYMBOL(key_unlink); EXPORT_SYMBOL(key_unlink);
/*
* Dispose of a keyring list after the RCU grace period, releasing the keys it
* links to.
*/
static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
{
struct keyring_list *klist;
int loop;
klist = container_of(rcu, struct keyring_list, rcu);
for (loop = klist->nkeys - 1; loop >= 0; loop--)
key_put(rcu_access_pointer(klist->keys[loop]));
kfree(klist);
}
/** /**
* keyring_clear - Clear a keyring * keyring_clear - Clear a keyring
* @keyring: The keyring to clear. * @keyring: The keyring to clear.
...@@ -1130,33 +1256,25 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu) ...@@ -1130,33 +1256,25 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
*/ */
int keyring_clear(struct key *keyring) int keyring_clear(struct key *keyring)
{ {
struct keyring_list *klist; struct assoc_array_edit *edit;
int ret; int ret;
ret = -ENOTDIR; if (keyring->type != &key_type_keyring)
if (keyring->type == &key_type_keyring) { return -ENOTDIR;
/* detach the pointer block with the locks held */
down_write(&keyring->sem);
klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* adjust the quota */
key_payload_reserve(keyring,
sizeof(struct keyring_list));
rcu_assign_pointer(keyring->payload.subscriptions,
NULL);
}
up_write(&keyring->sem);
/* free the keys after the locks have been dropped */ down_write(&keyring->sem);
if (klist)
call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops);
if (IS_ERR(edit)) {
ret = PTR_ERR(edit);
} else {
if (edit)
assoc_array_apply_edit(edit);
key_payload_reserve(keyring, 0);
ret = 0; ret = 0;
} }
up_write(&keyring->sem);
return ret; return ret;
} }
EXPORT_SYMBOL(keyring_clear); EXPORT_SYMBOL(keyring_clear);
...@@ -1168,17 +1286,25 @@ EXPORT_SYMBOL(keyring_clear); ...@@ -1168,17 +1286,25 @@ EXPORT_SYMBOL(keyring_clear);
*/ */
static void keyring_revoke(struct key *keyring) static void keyring_revoke(struct key *keyring)
{ {
struct keyring_list *klist; struct assoc_array_edit *edit;
klist = rcu_dereference_locked_keyring(keyring); edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops);
if (!IS_ERR(edit)) {
if (edit)
assoc_array_apply_edit(edit);
key_payload_reserve(keyring, 0);
}
}
/* adjust the quota */ static bool gc_iterator(void *object, void *iterator_data)
key_payload_reserve(keyring, 0); {
struct key *key = keyring_ptr_to_key(object);
time_t *limit = iterator_data;
if (klist) { if (key_is_dead(key, *limit))
rcu_assign_pointer(keyring->payload.subscriptions, NULL); return false;
call_rcu(&klist->rcu, keyring_clear_rcu_disposal); key_get(key);
} return true;
} }
/* /*
...@@ -1191,88 +1317,12 @@ static void keyring_revoke(struct key *keyring) ...@@ -1191,88 +1317,12 @@ static void keyring_revoke(struct key *keyring)
*/ */
void keyring_gc(struct key *keyring, time_t limit) void keyring_gc(struct key *keyring, time_t limit)
{ {
struct keyring_list *klist, *new;
struct key *key;
int loop, keep, max;
kenter("{%x,%s}", key_serial(keyring), keyring->description); kenter("{%x,%s}", key_serial(keyring), keyring->description);
down_write(&keyring->sem); down_write(&keyring->sem);
assoc_array_gc(&keyring->keys, &keyring_assoc_array_ops,
klist = rcu_dereference_locked_keyring(keyring); gc_iterator, &limit);
if (!klist)
goto no_klist;
/* work out how many subscriptions we're keeping */
keep = 0;
for (loop = klist->nkeys - 1; loop >= 0; loop--)
if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring),
limit))
keep++;
if (keep == klist->nkeys)
goto just_return;
/* allocate a new keyring payload */
max = roundup(keep, 4);
new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
GFP_KERNEL);
if (!new)
goto nomem;
new->maxkeys = max;
new->nkeys = 0;
new->delkey = 0;
/* install the live keys
* - must take care as expired keys may be updated back to life
*/
keep = 0;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
key = rcu_deref_link_locked(klist, loop, keyring);
if (!key_is_dead(key, limit)) {
if (keep >= max)
goto discard_new;
RCU_INIT_POINTER(new->keys[keep++], key_get(key));
}
}
new->nkeys = keep;
/* adjust the quota */
key_payload_reserve(keyring,
sizeof(struct keyring_list) +
KEYQUOTA_LINK_BYTES * keep);
if (keep == 0) {
rcu_assign_pointer(keyring->payload.subscriptions, NULL);
kfree(new);
} else {
rcu_assign_pointer(keyring->payload.subscriptions, new);
}
up_write(&keyring->sem);
call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
kleave(" [yes]");
return;
discard_new:
new->nkeys = keep;
keyring_clear_rcu_disposal(&new->rcu);
up_write(&keyring->sem); up_write(&keyring->sem);
kleave(" [discard]");
return;
just_return: kleave("");
up_write(&keyring->sem);
kleave(" [no dead]");
return;
no_klist:
up_write(&keyring->sem);
kleave(" [no_klist]");
return;
nomem:
up_write(&keyring->sem);
kleave(" [oom]");
} }
...@@ -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