-/* key.c: basic authentication token and access key management
+/* Basic authentication token and access key management
*
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-2007 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/module.h>
#include <linux/init.h>
+#include <linux/poison.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/security.h>
#include <linux/workqueue.h>
+#include <linux/random.h>
#include <linux/err.h>
#include "internal.h"
-static kmem_cache_t *key_jar;
-static key_serial_t key_serial_next = 3;
+static struct kmem_cache *key_jar;
struct rb_root key_serial_tree; /* tree of keys indexed by serial */
DEFINE_SPINLOCK(key_serial_lock);
static LIST_HEAD(key_types_list);
static DECLARE_RWSEM(key_types_sem);
-static void key_cleanup(void *data);
-static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL);
+static void key_cleanup(struct work_struct *work);
+static DECLARE_WORK(key_cleanup_task, key_cleanup);
/* we serialise key instantiation and link */
-DECLARE_RWSEM(key_construction_sem);
+DEFINE_MUTEX(key_construction_mutex);
/* any key who's type gets unegistered will be re-typed to this */
-struct key_type key_type_dead = {
+static struct key_type key_type_dead = {
.name = "dead",
};
candidate->qnkeys = 0;
candidate->qnbytes = 0;
spin_lock_init(&candidate->lock);
- INIT_LIST_HEAD(&candidate->consq);
+ mutex_init(&candidate->cons_lock);
rb_link_node(&candidate->node, parent, p);
rb_insert_color(&candidate->node, &key_user_tree);
found:
atomic_inc(&user->usage);
spin_unlock(&key_user_lock);
- if (candidate)
- kfree(candidate);
+ kfree(candidate);
out:
return user;
/*****************************************************************************/
/*
* assign a key the next unique serial number
- * - we work through all the serial numbers between 2 and 2^31-1 in turn and
- * then wrap
+ * - these are assigned randomly to avoid security issues through covert
+ * channel problems
*/
static inline void key_alloc_serial(struct key *key)
{
struct rb_node *parent, **p;
struct key *xkey;
- spin_lock(&key_serial_lock);
-
- /* propose a likely serial number and look for a hole for it in the
+ /* propose a random serial number and look for a hole for it in the
* serial number tree */
- key->serial = key_serial_next;
- if (key->serial < 3)
- key->serial = 3;
- key_serial_next = key->serial + 1;
+ do {
+ get_random_bytes(&key->serial, sizeof(key->serial));
+
+ key->serial >>= 1; /* negative numbers are not permitted */
+ } while (key->serial < 3);
+
+ spin_lock(&key_serial_lock);
+attempt_insertion:
parent = NULL;
p = &key_serial_tree.rb_node;
else
goto serial_exists;
}
- goto insert_here;
+
+ /* we've found a suitable hole - arrange for this key to occupy it */
+ rb_link_node(&key->serial_node, parent, p);
+ rb_insert_color(&key->serial_node, &key_serial_tree);
+
+ spin_unlock(&key_serial_lock);
+ return;
/* we found a key with the proposed serial number - walk the tree from
* that point looking for the next unused serial number */
- serial_exists:
+serial_exists:
for (;;) {
- key->serial = key_serial_next;
- if (key->serial < 2)
- key->serial = 2;
- key_serial_next = key->serial + 1;
-
- if (!parent->rb_parent)
- p = &key_serial_tree.rb_node;
- else if (parent->rb_parent->rb_left == parent)
- p = &parent->rb_parent->rb_left;
- else
- p = &parent->rb_parent->rb_right;
+ key->serial++;
+ if (key->serial < 3) {
+ key->serial = 3;
+ goto attempt_insertion;
+ }
parent = rb_next(parent);
if (!parent)
- break;
+ goto attempt_insertion;
xkey = rb_entry(parent, struct key, serial_node);
if (key->serial < xkey->serial)
- goto insert_here;
+ goto attempt_insertion;
}
- /* we've found a suitable hole - arrange for this key to occupy it */
- insert_here:
- rb_link_node(&key->serial_node, parent, p);
- rb_insert_color(&key->serial_node, &key_serial_tree);
-
- spin_unlock(&key_serial_lock);
-
} /* end key_alloc_serial() */
/*****************************************************************************/
/*
* allocate a key of the specified type
* - update the user's quota to reflect the existence of the key
- * - called from a key-type operation with key_types_sem read-locked by either
- * key_create_or_update() or by key_duplicate(); this prevents unregistration
- * of the key type
+ * - called from a key-type operation with key_types_sem read-locked by
+ * key_create_or_update()
+ * - this prevents unregistration of the key type
* - upon return the key is as yet uninstantiated; the caller needs to either
* instantiate the key or discard it before returning
*/
struct key *key_alloc(struct key_type *type, const char *desc,
- uid_t uid, gid_t gid, key_perm_t perm,
- int not_in_quota)
+ uid_t uid, gid_t gid, struct task_struct *ctx,
+ key_perm_t perm, unsigned long flags)
{
struct key_user *user = NULL;
struct key *key;
size_t desclen, quotalen;
+ int ret;
key = ERR_PTR(-EINVAL);
if (!desc || !*desc)
/* check that the user's quota permits allocation of another key and
* its description */
- if (!not_in_quota) {
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
spin_lock(&user->lock);
- if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS &&
- user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES
- )
- goto no_quota;
+ if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
+ if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS ||
+ user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES
+ )
+ goto no_quota;
+ }
user->qnkeys++;
user->qnbytes += quotalen;
}
/* allocate and initialise the key and its description */
- key = kmem_cache_alloc(key_jar, SLAB_KERNEL);
+ key = kmem_cache_alloc(key_jar, GFP_KERNEL);
if (!key)
goto no_memory_2;
if (desc) {
- key->description = kmalloc(desclen, GFP_KERNEL);
+ key->description = kmemdup(desc, desclen, GFP_KERNEL);
if (!key->description)
goto no_memory_3;
-
- memcpy(key->description, desc, desclen);
}
atomic_set(&key->usage, 1);
- rwlock_init(&key->lock);
init_rwsem(&key->sem);
key->type = type;
key->user = user;
key->flags = 0;
key->expiry = 0;
key->payload.data = NULL;
+ key->security = NULL;
- if (!not_in_quota)
- key->flags |= KEY_FLAG_IN_QUOTA;
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
+ key->flags |= 1 << KEY_FLAG_IN_QUOTA;
memset(&key->type_data, 0, sizeof(key->type_data));
key->magic = KEY_DEBUG_MAGIC;
#endif
+ /* let the security module know about the key */
+ ret = security_key_alloc(key, ctx, flags);
+ if (ret < 0)
+ goto security_error;
+
/* publish the key by giving it a serial number */
atomic_inc(&user->nkeys);
key_alloc_serial(key);
- error:
+error:
return key;
- no_memory_3:
+security_error:
+ kfree(key->description);
+ kmem_cache_free(key_jar, key);
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
+ spin_lock(&user->lock);
+ user->qnkeys--;
+ user->qnbytes -= quotalen;
+ spin_unlock(&user->lock);
+ }
+ key_user_put(user);
+ key = ERR_PTR(ret);
+ goto error;
+
+no_memory_3:
kmem_cache_free(key_jar, key);
- no_memory_2:
- if (!not_in_quota) {
+no_memory_2:
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
spin_lock(&user->lock);
user->qnkeys--;
user->qnbytes -= quotalen;
spin_unlock(&user->lock);
}
key_user_put(user);
- no_memory_1:
+no_memory_1:
key = ERR_PTR(-ENOMEM);
goto error;
- no_quota:
+no_quota:
spin_unlock(&user->lock);
key_user_put(user);
key = ERR_PTR(-EDQUOT);
key_check(key);
/* contemplate the quota adjustment */
- if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) {
+ if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
spin_lock(&key->user->lock);
if (delta > 0 &&
static int __key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
- struct key *keyring)
+ struct key *keyring,
+ struct key *instkey)
{
int ret, awaken;
awaken = 0;
ret = -EBUSY;
- down_write(&key_construction_sem);
+ mutex_lock(&key_construction_mutex);
/* can't instantiate twice */
- if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+ if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
/* instantiate the key */
ret = key->type->instantiate(key, data, datalen);
if (ret == 0) {
/* mark the key as being instantiated */
- write_lock(&key->lock);
-
atomic_inc(&key->user->nikeys);
- key->flags |= KEY_FLAG_INSTANTIATED;
+ set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
- if (key->flags & KEY_FLAG_USER_CONSTRUCT) {
- key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+ if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1;
- }
-
- write_unlock(&key->lock);
/* and link it into the destination keyring */
if (keyring)
ret = __key_link(keyring, key);
+
+ /* disable the authorisation key */
+ if (instkey)
+ key_revoke(instkey);
}
}
- up_write(&key_construction_sem);
+ mutex_unlock(&key_construction_mutex);
/* wake up anyone waiting for a key to be constructed */
if (awaken)
- wake_up_all(&request_key_conswq);
+ wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
return ret;
int key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
- struct key *keyring)
+ struct key *keyring,
+ struct key *instkey)
{
int ret;
if (keyring)
down_write(&keyring->sem);
- ret = __key_instantiate_and_link(key, data, datalen, keyring);
+ ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey);
if (keyring)
up_write(&keyring->sem);
return ret;
+
} /* end key_instantiate_and_link() */
EXPORT_SYMBOL(key_instantiate_and_link);
*/
int key_negate_and_link(struct key *key,
unsigned timeout,
- struct key *keyring)
+ struct key *keyring,
+ struct key *instkey)
{
struct timespec now;
int ret, awaken;
if (keyring)
down_write(&keyring->sem);
- down_write(&key_construction_sem);
+ mutex_lock(&key_construction_mutex);
/* can't instantiate twice */
- if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+ if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
/* mark the key as being negatively instantiated */
- write_lock(&key->lock);
-
atomic_inc(&key->user->nikeys);
- key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE;
+ set_bit(KEY_FLAG_NEGATIVE, &key->flags);
+ set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
now = current_kernel_time();
key->expiry = now.tv_sec + timeout;
- if (key->flags & KEY_FLAG_USER_CONSTRUCT) {
- key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+ if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1;
- }
- write_unlock(&key->lock);
ret = 0;
/* and link it into the destination keyring */
if (keyring)
ret = __key_link(keyring, key);
+
+ /* disable the authorisation key */
+ if (instkey)
+ key_revoke(instkey);
}
- up_write(&key_construction_sem);
+ mutex_unlock(&key_construction_mutex);
if (keyring)
up_write(&keyring->sem);
/* wake up anyone waiting for a key to be constructed */
if (awaken)
- wake_up_all(&request_key_conswq);
+ wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
return ret;
* do cleaning up in process context so that we don't have to disable
* interrupts all over the place
*/
-static void key_cleanup(void *data)
+static void key_cleanup(struct work_struct *work)
{
struct rb_node *_n;
struct key *key;
rb_erase(&key->serial_node, &key_serial_tree);
spin_unlock(&key_serial_lock);
+ key_check(key);
+
+ security_key_free(key);
+
/* deal with the user's key tracking and quota */
- if (key->flags & KEY_FLAG_IN_QUOTA) {
+ if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
spin_lock(&key->user->lock);
key->user->qnkeys--;
key->user->qnbytes -= key->quotalen;
}
atomic_dec(&key->user->nkeys);
- if (key->flags & KEY_FLAG_INSTANTIATED)
+ if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
atomic_dec(&key->user->nikeys);
key_user_put(key->user);
goto error;
found:
- /* pretent doesn't exist if it's dead */
+ /* pretend it doesn't exist if it's dead */
if (atomic_read(&key->usage) == 0 ||
- (key->flags & KEY_FLAG_DEAD) ||
+ test_bit(KEY_FLAG_DEAD, &key->flags) ||
key->type == &key_type_dead)
goto not_found;
* - the key has an incremented refcount
* - we need to put the key if we get an error
*/
-static inline struct key *__key_update(struct key *key, const void *payload,
- size_t plen)
+static inline key_ref_t __key_update(key_ref_t key_ref,
+ const void *payload, size_t plen)
{
+ struct key *key = key_ref_to_ptr(key_ref);
int ret;
/* need write permission on the key to update it */
- ret = -EACCES;
- if (!key_permission(key, KEY_WRITE))
+ ret = key_permission(key_ref, KEY_WRITE);
+ if (ret < 0)
goto error;
ret = -EEXIST;
down_write(&key->sem);
ret = key->type->update(key, payload, plen);
-
- if (ret == 0) {
+ if (ret == 0)
/* updating a negative key instantiates it */
- write_lock(&key->lock);
- key->flags &= ~KEY_FLAG_NEGATIVE;
- write_unlock(&key->lock);
- }
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
up_write(&key->sem);
if (ret < 0)
goto error;
- out:
- return key;
+out:
+ return key_ref;
- error:
+error:
key_put(key);
- key = ERR_PTR(ret);
+ key_ref = ERR_PTR(ret);
goto out;
} /* end __key_update() */
* search the specified keyring for a key of the same description; if one is
* found, update it, otherwise add a new one
*/
-struct key *key_create_or_update(struct key *keyring,
- const char *type,
- const char *description,
- const void *payload,
- size_t plen,
- int not_in_quota)
+key_ref_t key_create_or_update(key_ref_t keyring_ref,
+ const char *type,
+ const char *description,
+ const void *payload,
+ size_t plen,
+ unsigned long flags)
{
struct key_type *ktype;
- struct key *key = NULL;
+ struct key *keyring, *key = NULL;
key_perm_t perm;
+ key_ref_t key_ref;
int ret;
- key_check(keyring);
-
/* look up the key type to see if it's one of the registered kernel
* types */
ktype = key_type_lookup(type);
if (IS_ERR(ktype)) {
- key = ERR_PTR(-ENODEV);
+ key_ref = ERR_PTR(-ENODEV);
goto error;
}
- ret = -EINVAL;
+ key_ref = ERR_PTR(-EINVAL);
if (!ktype->match || !ktype->instantiate)
goto error_2;
- /* search for an existing key of the same type and description in the
- * destination keyring
- */
- down_write(&keyring->sem);
+ keyring = key_ref_to_ptr(keyring_ref);
- key = __keyring_search_one(keyring, ktype, description, 0);
- if (!IS_ERR(key))
- goto found_matching_key;
+ key_check(keyring);
- /* if we're going to allocate a new key, we're going to have to modify
- * the keyring */
- ret = -EACCES;
- if (!key_permission(keyring, KEY_WRITE))
+ key_ref = ERR_PTR(-ENOTDIR);
+ if (keyring->type != &key_type_keyring)
+ goto error_2;
+
+ down_write(&keyring->sem);
+
+ /* if we're going to allocate a new key, we're going to have
+ * to modify the keyring */
+ ret = key_permission(keyring_ref, KEY_WRITE);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
goto error_3;
+ }
+
+ /* if it's possible to update this type of key, search for an existing
+ * key of the same type and description in the destination keyring and
+ * update that instead if possible
+ */
+ if (ktype->update) {
+ key_ref = __keyring_search_one(keyring_ref, ktype, description,
+ 0);
+ if (!IS_ERR(key_ref))
+ goto found_matching_key;
+ }
/* decide on the permissions we want */
- perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK;
+ perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
+ perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK | KEY_USR_SETATTR;
if (ktype->read)
- perm |= KEY_USR_READ;
+ perm |= KEY_POS_READ | KEY_USR_READ;
if (ktype == &key_type_keyring || ktype->update)
perm |= KEY_USR_WRITE;
/* allocate a new key */
key = key_alloc(ktype, description, current->fsuid, current->fsgid,
- perm, not_in_quota);
+ current, perm, flags);
if (IS_ERR(key)) {
- ret = PTR_ERR(key);
+ key_ref = ERR_CAST(key);
goto error_3;
}
/* instantiate it and link it into the target keyring */
- ret = __key_instantiate_and_link(key, payload, plen, keyring);
+ ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL);
if (ret < 0) {
key_put(key);
- key = ERR_PTR(ret);
+ key_ref = ERR_PTR(ret);
+ goto error_3;
}
+ key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
+
error_3:
up_write(&keyring->sem);
error_2:
key_type_put(ktype);
error:
- return key;
+ return key_ref;
found_matching_key:
/* we found a matching key, so we're going to try to update it
up_write(&keyring->sem);
key_type_put(ktype);
- key = __key_update(key, payload, plen);
+ key_ref = __key_update(key_ref, payload, plen);
goto error;
} /* end key_create_or_update() */
/*
* update a key
*/
-int key_update(struct key *key, const void *payload, size_t plen)
+int key_update(key_ref_t key_ref, const void *payload, size_t plen)
{
+ struct key *key = key_ref_to_ptr(key_ref);
int ret;
key_check(key);
/* the key must be writable */
- ret = -EACCES;
- if (!key_permission(key, KEY_WRITE))
+ ret = key_permission(key_ref, KEY_WRITE);
+ if (ret < 0)
goto error;
/* attempt to update it if supported */
ret = -EOPNOTSUPP;
if (key->type->update) {
down_write(&key->sem);
- ret = key->type->update(key, payload, plen);
- if (ret == 0) {
+ ret = key->type->update(key, payload, plen);
+ if (ret == 0)
/* updating a negative key instantiates it */
- write_lock(&key->lock);
- key->flags &= ~KEY_FLAG_NEGATIVE;
- write_unlock(&key->lock);
- }
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
up_write(&key->sem);
}
/*****************************************************************************/
/*
- * duplicate a key, potentially with a revised description
- * - must be supported by the keytype (keyrings for instance can be duplicated)
- */
-struct key *key_duplicate(struct key *source, const char *desc)
-{
- struct key *key;
- int ret;
-
- key_check(source);
-
- if (!desc)
- desc = source->description;
-
- down_read(&key_types_sem);
-
- ret = -EINVAL;
- if (!source->type->duplicate)
- goto error;
-
- /* allocate and instantiate a key */
- key = key_alloc(source->type, desc, current->fsuid, current->fsgid,
- source->perm, 0);
- if (IS_ERR(key))
- goto error_k;
-
- down_read(&source->sem);
- ret = key->type->duplicate(key, source);
- up_read(&source->sem);
- if (ret < 0)
- goto error2;
-
- atomic_inc(&key->user->nikeys);
-
- write_lock(&key->lock);
- key->flags |= KEY_FLAG_INSTANTIATED;
- write_unlock(&key->lock);
-
- error_k:
- up_read(&key_types_sem);
- out:
- return key;
-
- error2:
- key_put(key);
- error:
- up_read(&key_types_sem);
- key = ERR_PTR(ret);
- goto out;
-
-} /* end key_duplicate() */
-
-/*****************************************************************************/
-/*
* revoke a key
*/
void key_revoke(struct key *key)
{
key_check(key);
- /* make sure no one's trying to change or use the key when we mark
- * it */
- down_write(&key->sem);
- write_lock(&key->lock);
- key->flags |= KEY_FLAG_REVOKED;
- write_unlock(&key->lock);
+ /* make sure no one's trying to change or use the key when we mark it
+ * - we tell lockdep that we might nest because we might be revoking an
+ * authorisation key whilst holding the sem on a key we've just
+ * instantiated
+ */
+ down_write_nested(&key->sem, 1);
+ if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) &&
+ key->type->revoke)
+ key->type->revoke(key);
+
up_write(&key->sem);
} /* end key_revoke() */
/* withdraw the key type */
list_del_init(&ktype->link);
- /* need to withdraw all keys of this type */
+ /* mark all the keys of this type dead */
spin_lock(&key_serial_lock);
for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
key = rb_entry(_n, struct key, serial_node);
- if (key->type != ktype)
- continue;
+ if (key->type == ktype)
+ key->type = &key_type_dead;
+ }
+
+ spin_unlock(&key_serial_lock);
+
+ /* make sure everyone revalidates their keys */
+ synchronize_rcu();
- write_lock(&key->lock);
- key->type = &key_type_dead;
- write_unlock(&key->lock);
+ /* we should now be able to destroy the payloads of all the keys of
+ * this type with impunity */
+ spin_lock(&key_serial_lock);
- /* there shouldn't be anyone looking at the description or
- * payload now */
- if (ktype->destroy)
- ktype->destroy(key);
- memset(&key->payload, 0xbd, sizeof(key->payload));
+ for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+ key = rb_entry(_n, struct key, serial_node);
+
+ if (key->type == ktype) {
+ if (ktype->destroy)
+ ktype->destroy(key);
+ memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
+ }
}
spin_unlock(&key_serial_lock);
{
/* allocate a slab in which we can store keys */
key_jar = kmem_cache_create("key_jar", sizeof(struct key),
- 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
/* add the special key types */
list_add_tail(&key_type_keyring.link, &key_types_list);
/* link the two root keyrings together */
key_link(&root_session_keyring, &root_user_keyring);
+
} /* end key_init() */