-/* keyring.c: keyring handling
+/* Keyring handling
*
- * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/err.h>
+#include <keys/keyring-type.h>
#include <asm/uaccess.h>
#include "internal.h"
*/
static int keyring_instantiate(struct key *keyring,
const void *data, size_t datalen);
-static int keyring_duplicate(struct key *keyring, const struct key *source);
static int keyring_match(const struct key *keyring, const void *criterion);
+static void keyring_revoke(struct key *keyring);
static void keyring_destroy(struct key *keyring);
static void keyring_describe(const struct key *keyring, struct seq_file *m);
static long keyring_read(const struct key *keyring,
.name = "keyring",
.def_datalen = sizeof(struct keyring_list),
.instantiate = keyring_instantiate,
- .duplicate = keyring_duplicate,
.match = keyring_match,
+ .revoke = keyring_revoke,
.destroy = keyring_destroy,
.describe = keyring_describe,
.read = keyring_read,
};
+EXPORT_SYMBOL(key_type_keyring);
+
/*
* semaphore to serialise link/link calls to prevent two link calls in parallel
* introducing a cycle
*/
-DECLARE_RWSEM(keyring_serialise_link_sem);
+static DECLARE_RWSEM(keyring_serialise_link_sem);
/*****************************************************************************/
/*
* publish the name of a keyring so that it can be found by name (if it has
* one)
*/
-void keyring_publish_name(struct key *keyring)
+static void keyring_publish_name(struct key *keyring)
{
int bucket;
} /* end keyring_instantiate() */
-/*****************************************************************************/
-/*
- * duplicate the list of subscribed keys from a source keyring into this one
- */
-static int keyring_duplicate(struct key *keyring, const struct key *source)
-{
- struct keyring_list *sklist, *klist;
- unsigned max;
- size_t size;
- int loop, ret;
-
- const unsigned limit =
- (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
-
- ret = 0;
-
- /* find out how many keys are currently linked */
- rcu_read_lock();
- sklist = rcu_dereference(source->payload.subscriptions);
- max = 0;
- if (sklist)
- max = sklist->nkeys;
- rcu_read_unlock();
-
- /* allocate a new payload and stuff load with key links */
- if (max > 0) {
- BUG_ON(max > limit);
-
- max = (max + 3) & ~3;
- if (max > limit)
- max = limit;
-
- ret = -ENOMEM;
- size = sizeof(*klist) + sizeof(struct key) * max;
- klist = kmalloc(size, GFP_KERNEL);
- if (!klist)
- goto error;
-
- /* set links */
- rcu_read_lock();
- sklist = rcu_dereference(source->payload.subscriptions);
-
- klist->maxkeys = max;
- klist->nkeys = sklist->nkeys;
- memcpy(klist->keys,
- sklist->keys,
- sklist->nkeys * sizeof(struct key));
-
- for (loop = klist->nkeys - 1; loop >= 0; loop--)
- atomic_inc(&klist->keys[loop]->usage);
-
- rcu_read_unlock();
-
- rcu_assign_pointer(keyring->payload.subscriptions, klist);
- ret = 0;
- }
-
- error:
- return ret;
-
-} /* end keyring_duplicate() */
-
/*****************************************************************************/
/*
* match keyrings on their name
if (keyring->description) {
write_lock(&keyring_name_lock);
- list_del(&keyring->type_data.link);
+
+ if (keyring->type_data.link.next != NULL &&
+ !list_empty(&keyring->type_data.link))
+ list_del(&keyring->type_data.link);
+
write_unlock(&keyring_name_lock);
}
* allocate a keyring and link into the destination keyring
*/
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- int not_in_quota, struct key *dest)
+ const struct cred *cred, unsigned long flags,
+ struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid, KEY_USR_ALL, not_in_quota);
+ uid, gid, cred,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
+ flags);
if (!IS_ERR(keyring)) {
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
* - we rely on RCU to prevent the keyring lists from disappearing on us
* - we return -EAGAIN if we didn't find any matching key
* - we return -ENOKEY if we only found negative matching keys
+ * - we propagate the possession attribute from the keyring ref to the key ref
*/
-struct key *keyring_search_aux(struct key *keyring,
- struct task_struct *context,
- struct key_type *type,
- const void *description,
- key_match_func_t match)
+key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+ const struct cred *cred,
+ struct key_type *type,
+ const void *description,
+ key_match_func_t match)
{
struct {
struct keyring_list *keylist;
struct keyring_list *keylist;
struct timespec now;
- struct key *key;
+ unsigned long possessed, kflags;
+ struct key *keyring, *key;
+ key_ref_t key_ref;
long err;
int sp, kix;
+ keyring = key_ref_to_ptr(keyring_ref);
+ possessed = is_key_possessed(keyring_ref);
key_check(keyring);
- rcu_read_lock();
-
/* top keyring must have search permission to begin the search */
- key = ERR_PTR(-EACCES);
- if (!key_task_permission(keyring, context, KEY_SEARCH))
+ err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
+ if (err < 0) {
+ key_ref = ERR_PTR(err);
goto error;
+ }
- key = ERR_PTR(-ENOTDIR);
+ key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring)
goto error;
+ rcu_read_lock();
+
now = current_kernel_time();
err = -EAGAIN;
sp = 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 == type && match(keyring, description)) {
+ key = keyring;
+
+ /* check it isn't negative and hasn't expired or been
+ * revoked */
+ if (kflags & (1 << KEY_FLAG_REVOKED))
+ goto error_2;
+ if (key->expiry && now.tv_sec >= key->expiry)
+ goto error_2;
+ key_ref = ERR_PTR(-ENOKEY);
+ if (kflags & (1 << KEY_FLAG_NEGATIVE))
+ goto error_2;
+ goto found;
+ }
+
+ /* otherwise, the top keyring must not be revoked, expired, or
+ * negatively instantiated if we are to search it */
+ key_ref = ERR_PTR(-EAGAIN);
+ if (kflags & ((1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_NEGATIVE)) ||
+ (keyring->expiry && now.tv_sec >= keyring->expiry))
+ goto error_2;
+
/* start processing a new keyring */
- descend:
+descend:
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
goto not_this_keyring;
/* iterate through the keys in this keyring first */
for (kix = 0; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
+ kflags = key->flags;
/* ignore keys not of this type */
if (key->type != type)
continue;
/* skip revoked keys and expired keys */
- if (test_bit(KEY_FLAG_REVOKED, &key->flags))
+ if (kflags & (1 << KEY_FLAG_REVOKED))
continue;
if (key->expiry && now.tv_sec >= key->expiry)
continue;
/* key must have search permissions */
- if (!key_task_permission(key, context, KEY_SEARCH))
+ if (key_task_permission(make_key_ref(key, possessed),
+ cred, KEY_SEARCH) < 0)
continue;
- /* we set a different error code if we find a negative key */
- if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
+ /* we set a different error code if we pass a negative key */
+ if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
err = -ENOKEY;
continue;
}
/* search through the keyrings nested in this one */
kix = 0;
- ascend:
+ascend:
for (; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
if (key->type != &key_type_keyring)
if (sp >= KEYRING_SEARCH_MAX_DEPTH)
continue;
- if (!key_task_permission(key, context, KEY_SEARCH))
+ if (key_task_permission(make_key_ref(key, possessed),
+ cred, KEY_SEARCH) < 0)
continue;
/* stack the current position */
/* the keyring we're looking at was disqualified or didn't contain a
* matching key */
- not_this_keyring:
+not_this_keyring:
if (sp > 0) {
/* resume the processing of a keyring higher up in the tree */
sp--;
goto ascend;
}
- key = ERR_PTR(err);
- goto error;
+ key_ref = ERR_PTR(err);
+ goto error_2;
/* we found a viable match */
- found:
+found:
atomic_inc(&key->usage);
key_check(key);
- error:
+ key_ref = make_key_ref(key, possessed);
+error_2:
rcu_read_unlock();
- return key;
+error:
+ return key_ref;
} /* end keyring_search_aux() */
* - we return -EAGAIN if we didn't find any matching key
* - we return -ENOKEY if we only found negative matching keys
*/
-struct key *keyring_search(struct key *keyring,
- struct key_type *type,
- const char *description)
+key_ref_t keyring_search(key_ref_t keyring,
+ struct key_type *type,
+ const char *description)
{
if (!type->match)
return ERR_PTR(-ENOKEY);
- return keyring_search_aux(keyring, current,
+ return keyring_search_aux(keyring, current->cred,
type, description, type->match);
} /* end keyring_search() */
/*
* search the given keyring only (no recursion)
* - keyring must be locked by caller
+ * - caller must guarantee that the keyring is a keyring
*/
-struct key *__keyring_search_one(struct key *keyring,
- const struct key_type *ktype,
- const char *description,
- key_perm_t perm)
+key_ref_t __keyring_search_one(key_ref_t keyring_ref,
+ const struct key_type *ktype,
+ const char *description,
+ key_perm_t perm)
{
struct keyring_list *klist;
- struct key *key;
+ unsigned long possessed;
+ struct key *keyring, *key;
int loop;
+ keyring = key_ref_to_ptr(keyring_ref);
+ possessed = is_key_possessed(keyring_ref);
+
rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
if (key->type == ktype &&
(!key->type->match ||
key->type->match(key, description)) &&
- key_permission(key, perm) &&
+ key_permission(make_key_ref(key, possessed),
+ perm) == 0 &&
!test_bit(KEY_FLAG_REVOKED, &key->flags)
)
goto found;
}
}
- key = ERR_PTR(-ENOKEY);
- goto error;
+ rcu_read_unlock();
+ return ERR_PTR(-ENOKEY);
found:
atomic_inc(&key->usage);
- error:
rcu_read_unlock();
- return key;
+ return make_key_ref(key, possessed);
} /* end __keyring_search_one() */
-/*****************************************************************************/
-/*
- * search for an instantiation authorisation key matching a target key
- * - the RCU read lock must be held by the caller
- * - a target_id of zero specifies any valid token
- */
-struct key *keyring_search_instkey(struct key *keyring,
- key_serial_t target_id)
-{
- struct request_key_auth *rka;
- struct keyring_list *klist;
- struct key *instkey;
- int loop;
-
- klist = rcu_dereference(keyring->payload.subscriptions);
- if (klist) {
- for (loop = 0; loop < klist->nkeys; loop++) {
- instkey = klist->keys[loop];
-
- if (instkey->type != &key_type_request_key_auth)
- continue;
-
- rka = instkey->payload.data;
- if (target_id && rka->target_key->serial != target_id)
- continue;
-
- /* the auth key is revoked during instantiation */
- if (!test_bit(KEY_FLAG_REVOKED, &instkey->flags))
- goto found;
-
- instkey = ERR_PTR(-EKEYREVOKED);
- goto error;
- }
- }
-
- instkey = ERR_PTR(-EACCES);
- goto error;
-
-found:
- atomic_inc(&instkey->usage);
-error:
- return instkey;
-
-} /* end keyring_search_instkey() */
-
/*****************************************************************************/
/*
* find a keyring with the specified name
* - all named keyrings are searched
- * - only find keyrings with search permission for the process
- * - only find keyrings with a serial number greater than the one specified
+ * - normally only finds keyrings with search permission for the current process
*/
-struct key *find_keyring_by_name(const char *name, key_serial_t bound)
+struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
{
struct key *keyring;
int bucket;
&keyring_name_hash[bucket],
type_data.link
) {
- if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ if (keyring->user->user_ns != current_user_ns())
continue;
- if (strcmp(keyring->description, name) != 0)
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
continue;
- if (!key_permission(keyring, KEY_SEARCH))
+ if (strcmp(keyring->description, name) != 0)
continue;
- /* found a potential candidate, but we still need to
- * check the serial number */
- if (keyring->serial <= bound)
+ if (!skip_perm_check &&
+ key_permission(make_key_ref(keyring, 0),
+ KEY_SEARCH) < 0)
continue;
/* we've got a match */
} /* end keyring_link_rcu_disposal() */
+/*****************************************************************************/
+/*
+ * 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);
+
+ key_put(klist->keys[klist->delkey]);
+ kfree(klist);
+
+} /* end keyring_unlink_rcu_disposal() */
+
/*****************************************************************************/
/*
* link a key into to a keyring
* - must be called with the keyring's semaphore write-locked
+ * - discard already extant link to matching key if there is one
*/
int __key_link(struct key *keyring, struct key *key)
{
struct keyring_list *klist, *nklist;
unsigned max;
size_t size;
- int ret;
+ int loop, ret;
ret = -EKEYREVOKED;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
goto error2;
}
+ /* see if there's a matching key we can displace */
+ klist = keyring->payload.subscriptions;
+
+ if (klist && klist->nkeys > 0) {
+ struct key_type *type = key->type;
+
+ for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+ if (klist->keys[loop]->type == type &&
+ strcmp(klist->keys[loop]->description,
+ key->description) == 0
+ ) {
+ /* found a match - replace with new key */
+ size = sizeof(struct key *) * klist->maxkeys;
+ size += sizeof(*klist);
+ BUG_ON(size > PAGE_SIZE);
+
+ ret = -ENOMEM;
+ nklist = kmemdup(klist, size, GFP_KERNEL);
+ if (!nklist)
+ goto error2;
+
+ /* replace matched key */
+ atomic_inc(&key->usage);
+ nklist->keys[loop] = key;
+
+ rcu_assign_pointer(
+ keyring->payload.subscriptions,
+ nklist);
+
+ /* dispose of the old keyring list and the
+ * displaced key */
+ klist->delkey = loop;
+ call_rcu(&klist->rcu,
+ keyring_unlink_rcu_disposal);
+
+ goto done;
+ }
+ }
+ }
+
/* check that we aren't going to overrun the user's quota */
ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES);
smp_wmb();
klist->nkeys++;
smp_wmb();
-
- ret = 0;
}
else {
/* grow the key list */
ret = -ENFILE;
if (max > 65535)
goto error3;
- size = sizeof(*klist) + sizeof(*key) * max;
+ size = sizeof(*klist) + sizeof(struct key *) * max;
if (size > PAGE_SIZE)
goto error3;
/* dispose of the old keyring list */
if (klist)
call_rcu(&klist->rcu, keyring_link_rcu_disposal);
-
- ret = 0;
}
- error2:
+done:
+ ret = 0;
+error2:
up_write(&keyring_serialise_link_sem);
- error:
+error:
return ret;
- error3:
+error3:
/* undo the quota changes */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
EXPORT_SYMBOL(key_link);
-/*****************************************************************************/
-/*
- * 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);
-
- key_put(klist->keys[klist->delkey]);
- kfree(klist);
-
-} /* end keyring_unlink_rcu_disposal() */
-
/*****************************************************************************/
/*
* unlink the first link to a key from a keyring
key_is_present:
/* we need to copy the key list for RCU purposes */
- nklist = kmalloc(sizeof(*klist) + sizeof(*key) * klist->maxkeys,
+ nklist = kmalloc(sizeof(*klist) +
+ sizeof(struct key *) * klist->maxkeys,
GFP_KERNEL);
if (!nklist)
goto nomem;
if (loop > 0)
memcpy(&nklist->keys[0],
&klist->keys[0],
- loop * sizeof(klist->keys[0]));
+ loop * sizeof(struct key *));
if (loop < nklist->nkeys)
memcpy(&nklist->keys[loop],
&klist->keys[loop + 1],
- (nklist->nkeys - loop) * sizeof(klist->keys[0]));
+ (nklist->nkeys - loop) * sizeof(struct key *));
/* adjust the user's quota */
key_payload_reserve(keyring,
} /* end keyring_clear() */
EXPORT_SYMBOL(keyring_clear);
+
+/*****************************************************************************/
+/*
+ * dispose of the links from a revoked keyring
+ * - called with the key sem write-locked
+ */
+static void keyring_revoke(struct key *keyring)
+{
+ struct keyring_list *klist = keyring->payload.subscriptions;
+
+ /* adjust the quota */
+ key_payload_reserve(keyring, 0);
+
+ if (klist) {
+ rcu_assign_pointer(keyring->payload.subscriptions, NULL);
+ call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+ }
+
+} /* end keyring_revoke() */
+
+/*
+ * Determine whether a key is dead
+ */
+static bool key_is_dead(struct key *key, time_t limit)
+{
+ return test_bit(KEY_FLAG_DEAD, &key->flags) ||
+ (key->expiry > 0 && key->expiry <= limit);
+}
+
+/*
+ * Collect garbage from the contents of a keyring
+ */
+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);
+
+ down_write(&keyring->sem);
+
+ klist = keyring->payload.subscriptions;
+ 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(klist->keys[loop], 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 = klist->keys[loop];
+ if (!key_is_dead(key, limit)) {
+ if (keep >= max)
+ goto discard_new;
+ 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);
+ kleave(" [discard]");
+ return;
+
+just_return:
+ 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]");
+}