summaryrefslogtreecommitdiff
path: root/security/keys
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
committerScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
commit62b8c978ee6b8d135d9e7953221de58000dba986 (patch)
tree683b04b2e627f6710c22c151b23c8cc9a165315e /security/keys
parent78fd82238d0e5716578c326404184a27ba67fd6e (diff)
downloadlinux-fsl-qoriq-62b8c978ee6b8d135d9e7953221de58000dba986.tar.xz
Rewind v3.13-rc3+ (78fd82238d0e5716) to v3.12
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/Kconfig29
-rw-r--r--security/keys/Makefile2
-rw-r--r--security/keys/big_key.c207
-rw-r--r--security/keys/compat.c3
-rw-r--r--security/keys/gc.c47
-rw-r--r--security/keys/internal.h74
-rw-r--r--security/keys/key.c102
-rw-r--r--security/keys/keyctl.c3
-rw-r--r--security/keys/keyring.c1536
-rw-r--r--security/keys/persistent.c167
-rw-r--r--security/keys/proc.c17
-rw-r--r--security/keys/process_keys.c141
-rw-r--r--security/keys/request_key.c60
-rw-r--r--security/keys/request_key_auth.c31
-rw-r--r--security/keys/sysctl.c11
-rw-r--r--security/keys/user_defined.c18
16 files changed, 969 insertions, 1479 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index a4f3f8c..a90d6d3 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -4,7 +4,6 @@
config KEYS
bool "Enable access key retention support"
- select ASSOCIATIVE_ARRAY
help
This option provides support for retaining authentication tokens and
access keys in the kernel.
@@ -20,34 +19,6 @@ config KEYS
If you are unsure as to whether this is required, answer N.
-config PERSISTENT_KEYRINGS
- bool "Enable register of persistent per-UID keyrings"
- depends on KEYS
- help
- This option provides a register of persistent per-UID keyrings,
- primarily aimed at Kerberos key storage. The keyrings are persistent
- in the sense that they stay around after all processes of that UID
- have exited, not that they survive the machine being rebooted.
-
- A particular keyring may be accessed by either the user whose keyring
- it is or by a process with administrative privileges. The active
- LSMs gets to rule on which admin-level processes get to access the
- cache.
-
- Keyrings are created and added into the register upon demand and get
- removed if they expire (a default timeout is set upon creation).
-
-config BIG_KEYS
- bool "Large payload keys"
- depends on KEYS
- depends on TMPFS
- help
- This option provides support for holding large keys within the kernel
- (for example Kerberos ticket caches). The data may be stored out to
- swapspace by tmpfs.
-
- If you are unsure as to whether this is required, answer N.
-
config TRUSTED_KEYS
tristate "TRUSTED KEYS"
depends on KEYS && TCG_TPM
diff --git a/security/keys/Makefile b/security/keys/Makefile
index dfb3a7b..504aaa0 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -18,11 +18,9 @@ obj-y := \
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
-obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
#
# Key types
#
-obj-$(CONFIG_BIG_KEYS) += big_key.o
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
deleted file mode 100644
index 7f44c32..0000000
--- a/security/keys/big_key.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/* Large capacity key type
- *
- * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/seq_file.h>
-#include <linux/file.h>
-#include <linux/shmem_fs.h>
-#include <linux/err.h>
-#include <keys/user-type.h>
-#include <keys/big_key-type.h>
-
-MODULE_LICENSE("GPL");
-
-/*
- * If the data is under this limit, there's no point creating a shm file to
- * hold it as the permanently resident metadata for the shmem fs will be at
- * least as large as the data.
- */
-#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
-
-/*
- * big_key defined keys take an arbitrary string as the description and an
- * arbitrary blob of data as the payload
- */
-struct key_type key_type_big_key = {
- .name = "big_key",
- .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
- .instantiate = big_key_instantiate,
- .match = user_match,
- .revoke = big_key_revoke,
- .destroy = big_key_destroy,
- .describe = big_key_describe,
- .read = big_key_read,
-};
-
-/*
- * Instantiate a big key
- */
-int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
-{
- struct path *path = (struct path *)&key->payload.data2;
- struct file *file;
- ssize_t written;
- size_t datalen = prep->datalen;
- int ret;
-
- ret = -EINVAL;
- if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
- goto error;
-
- /* Set an arbitrary quota */
- ret = key_payload_reserve(key, 16);
- if (ret < 0)
- goto error;
-
- key->type_data.x[1] = datalen;
-
- if (datalen > BIG_KEY_FILE_THRESHOLD) {
- /* Create a shmem file to store the data in. This will permit the data
- * to be swapped out if needed.
- *
- * TODO: Encrypt the stored data with a temporary key.
- */
- file = shmem_file_setup("", datalen, 0);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto err_quota;
- }
-
- written = kernel_write(file, prep->data, prep->datalen, 0);
- if (written != datalen) {
- ret = written;
- if (written >= 0)
- ret = -ENOMEM;
- goto err_fput;
- }
-
- /* Pin the mount and dentry to the key so that we can open it again
- * later
- */
- *path = file->f_path;
- path_get(path);
- fput(file);
- } else {
- /* Just store the data in a buffer */
- void *data = kmalloc(datalen, GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto err_quota;
- }
-
- key->payload.data = memcpy(data, prep->data, prep->datalen);
- }
- return 0;
-
-err_fput:
- fput(file);
-err_quota:
- key_payload_reserve(key, 0);
-error:
- return ret;
-}
-
-/*
- * dispose of the links from a revoked keyring
- * - called with the key sem write-locked
- */
-void big_key_revoke(struct key *key)
-{
- struct path *path = (struct path *)&key->payload.data2;
-
- /* clear the quota */
- key_payload_reserve(key, 0);
- if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
- vfs_truncate(path, 0);
-}
-
-/*
- * dispose of the data dangling from the corpse of a big_key key
- */
-void big_key_destroy(struct key *key)
-{
- if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
- struct path *path = (struct path *)&key->payload.data2;
- path_put(path);
- path->mnt = NULL;
- path->dentry = NULL;
- } else {
- kfree(key->payload.data);
- key->payload.data = NULL;
- }
-}
-
-/*
- * describe the big_key key
- */
-void big_key_describe(const struct key *key, struct seq_file *m)
-{
- unsigned long datalen = key->type_data.x[1];
-
- seq_puts(m, key->description);
-
- if (key_is_instantiated(key))
- seq_printf(m, ": %lu [%s]",
- datalen,
- datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
-}
-
-/*
- * read the key data
- * - the key's semaphore is read-locked
- */
-long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
-{
- unsigned long datalen = key->type_data.x[1];
- long ret;
-
- if (!buffer || buflen < datalen)
- return datalen;
-
- if (datalen > BIG_KEY_FILE_THRESHOLD) {
- struct path *path = (struct path *)&key->payload.data2;
- struct file *file;
- loff_t pos;
-
- file = dentry_open(path, O_RDONLY, current_cred());
- if (IS_ERR(file))
- return PTR_ERR(file);
-
- pos = 0;
- ret = vfs_read(file, buffer, datalen, &pos);
- fput(file);
- if (ret >= 0 && ret != datalen)
- ret = -EIO;
- } else {
- ret = datalen;
- if (copy_to_user(buffer, key->payload.data, datalen) != 0)
- ret = -EFAULT;
- }
-
- return ret;
-}
-
-/*
- * Module stuff
- */
-static int __init big_key_init(void)
-{
- return register_key_type(&key_type_big_key);
-}
-
-static void __exit big_key_cleanup(void)
-{
- unregister_key_type(&key_type_big_key);
-}
-
-module_init(big_key_init);
-module_exit(big_key_cleanup);
diff --git a/security/keys/compat.c b/security/keys/compat.c
index bbd32c7..d65fa7f 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -138,9 +138,6 @@ asmlinkage long compat_sys_keyctl(u32 option,
case KEYCTL_INVALIDATE:
return keyctl_invalidate_key(arg2);
- case KEYCTL_GET_PERSISTENT:
- return keyctl_get_persistent(arg2, arg3);
-
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/gc.c b/security/keys/gc.c
index d3222b6..d67c97b 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -131,6 +131,50 @@ void key_gc_keytype(struct key_type *ktype)
}
/*
+ * Garbage collect pointers from a keyring.
+ *
+ * Not called with any locks held. The keyring's key struct will not be
+ * deallocated under us as only our caller may deallocate it.
+ */
+static void key_gc_keyring(struct key *keyring, time_t limit)
+{
+ struct keyring_list *klist;
+ int loop;
+
+ kenter("%x", key_serial(keyring));
+
+ if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
+ goto dont_gc;
+
+ /* scan the keyring looking for dead keys */
+ rcu_read_lock();
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (!klist)
+ goto unlock_dont_gc;
+
+ loop = klist->nkeys;
+ smp_rmb();
+ for (loop--; loop >= 0; loop--) {
+ struct key *key = rcu_dereference(klist->keys[loop]);
+ if (key_is_dead(key, limit))
+ goto do_gc;
+ }
+
+unlock_dont_gc:
+ rcu_read_unlock();
+dont_gc:
+ kleave(" [no gc]");
+ return;
+
+do_gc:
+ rcu_read_unlock();
+
+ keyring_gc(keyring, limit);
+ kleave(" [gc]");
+}
+
+/*
* Garbage collect a list of unreferenced, detached keys
*/
static noinline void key_gc_unused_keys(struct list_head *keys)
@@ -348,7 +392,8 @@ found_unreferenced_key:
*/
found_keyring:
spin_unlock(&key_serial_lock);
- keyring_gc(key, limit);
+ kdebug("scan keyring %d", key->serial);
+ key_gc_keyring(key, limit);
goto maybe_resched;
/* We found a dead key that is still referenced. Reset its type and
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 80b2aac..d4f1468 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -89,53 +89,42 @@ extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype);
extern int __key_link_begin(struct key *keyring,
- const struct keyring_index_key *index_key,
- struct assoc_array_edit **_edit);
+ const struct key_type *type,
+ const char *description,
+ unsigned long *_prealloc);
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
-extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
+extern void __key_link(struct key *keyring, struct key *key,
+ unsigned long *_prealloc);
extern void __key_link_end(struct key *keyring,
- const struct keyring_index_key *index_key,
- struct assoc_array_edit *edit);
+ struct key_type *type,
+ unsigned long prealloc);
-extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
- const struct keyring_index_key *index_key);
+extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
+ const struct key_type *type,
+ const char *description,
+ key_perm_t perm);
extern struct key *keyring_search_instkey(struct key *keyring,
key_serial_t target_id);
-extern int iterate_over_keyring(const struct key *keyring,
- int (*func)(const struct key *key, void *data),
- void *data);
-
typedef int (*key_match_func_t)(const struct key *, const void *);
-struct keyring_search_context {
- struct keyring_index_key index_key;
- const struct cred *cred;
- key_match_func_t match;
- const void *match_data;
- unsigned flags;
-#define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */
-#define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */
-#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */
-#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */
-#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */
-#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */
-
- int (*iterator)(const void *object, void *iterator_data);
-
- /* Internal stuff */
- int skipped_ret;
- bool possessed;
- key_ref_t result;
- struct timespec now;
-};
-
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct keyring_search_context *ctx);
-
-extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
-extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
+ const struct cred *cred,
+ struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ bool no_state_check);
+
+extern key_ref_t search_my_process_keyrings(struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ bool no_state_check,
+ const struct cred *cred);
+extern key_ref_t search_process_keyrings(struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ const struct cred *cred);
extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
@@ -213,7 +202,7 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
/*
* Determine whether a key is dead.
*/
-static inline bool key_is_dead(const struct key *key, time_t limit)
+static inline bool key_is_dead(struct key *key, time_t limit)
{
return
key->flags & ((1 << KEY_FLAG_DEAD) |
@@ -255,15 +244,6 @@ extern long keyctl_invalidate_key(key_serial_t);
extern long keyctl_instantiate_key_common(key_serial_t,
const struct iovec *,
unsigned, size_t, key_serial_t);
-#ifdef CONFIG_PERSISTENT_KEYRINGS
-extern long keyctl_get_persistent(uid_t, key_serial_t);
-extern unsigned persistent_keyring_expiry;
-#else
-static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
-{
- return -EOPNOTSUPP;
-}
-#endif
/*
* Debugging key validation
diff --git a/security/keys/key.c b/security/keys/key.c
index 55d110f..8fb7c7b 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -242,8 +242,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
}
}
- desclen = strlen(desc);
- quotalen = desclen + 1 + type->def_datalen;
+ desclen = strlen(desc) + 1;
+ quotalen = desclen + type->def_datalen;
/* get hold of the key tracking for this user */
user = key_user_lookup(uid);
@@ -277,8 +277,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
goto no_memory_2;
if (desc) {
- key->index_key.desc_len = desclen;
- key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
+ key->description = kmemdup(desc, desclen, GFP_KERNEL);
if (!key->description)
goto no_memory_3;
}
@@ -286,7 +285,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
atomic_set(&key->usage, 1);
init_rwsem(&key->sem);
lockdep_set_class(&key->sem, &type->lock_class);
- key->index_key.type = type;
+ key->type = type;
key->user = user;
key->quotalen = quotalen;
key->datalen = type->def_datalen;
@@ -300,8 +299,6 @@ struct key *key_alloc(struct key_type *type, const char *desc,
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
- if (flags & KEY_ALLOC_TRUSTED)
- key->flags |= 1 << KEY_FLAG_TRUSTED;
memset(&key->type_data, 0, sizeof(key->type_data));
@@ -411,7 +408,7 @@ static int __key_instantiate_and_link(struct key *key,
struct key_preparsed_payload *prep,
struct key *keyring,
struct key *authkey,
- struct assoc_array_edit **_edit)
+ unsigned long *_prealloc)
{
int ret, awaken;
@@ -438,7 +435,7 @@ static int __key_instantiate_and_link(struct key *key,
/* and link it into the destination keyring */
if (keyring)
- __key_link(key, _edit);
+ __key_link(keyring, key, _prealloc);
/* disable the authorisation key */
if (authkey)
@@ -478,7 +475,7 @@ int key_instantiate_and_link(struct key *key,
struct key *authkey)
{
struct key_preparsed_payload prep;
- struct assoc_array_edit *edit;
+ unsigned long prealloc;
int ret;
memset(&prep, 0, sizeof(prep));
@@ -492,15 +489,17 @@ int key_instantiate_and_link(struct key *key,
}
if (keyring) {
- ret = __key_link_begin(keyring, &key->index_key, &edit);
+ ret = __key_link_begin(keyring, key->type, key->description,
+ &prealloc);
if (ret < 0)
goto error_free_preparse;
}
- ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
+ ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
+ &prealloc);
if (keyring)
- __key_link_end(keyring, &key->index_key, edit);
+ __key_link_end(keyring, key->type, prealloc);
error_free_preparse:
if (key->type->preparse)
@@ -538,7 +537,7 @@ int key_reject_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
- struct assoc_array_edit *edit;
+ unsigned long prealloc;
struct timespec now;
int ret, awaken, link_ret = 0;
@@ -549,7 +548,8 @@ int key_reject_and_link(struct key *key,
ret = -EBUSY;
if (keyring)
- link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+ link_ret = __key_link_begin(keyring, key->type,
+ key->description, &prealloc);
mutex_lock(&key_construction_mutex);
@@ -557,10 +557,9 @@ int key_reject_and_link(struct key *key,
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
/* mark the key as being negatively instantiated */
atomic_inc(&key->user->nikeys);
- key->type_data.reject_error = -error;
- smp_wmb();
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+ key->type_data.reject_error = -error;
now = current_kernel_time();
key->expiry = now.tv_sec + timeout;
key_schedule_gc(key->expiry + key_gc_delay);
@@ -572,7 +571,7 @@ int key_reject_and_link(struct key *key,
/* and link it into the destination keyring */
if (keyring && link_ret == 0)
- __key_link(key, &edit);
+ __key_link(keyring, key, &prealloc);
/* disable the authorisation key */
if (authkey)
@@ -582,7 +581,7 @@ int key_reject_and_link(struct key *key,
mutex_unlock(&key_construction_mutex);
if (keyring)
- __key_link_end(keyring, &key->index_key, edit);
+ __key_link_end(keyring, key->type, prealloc);
/* wake up anyone waiting for a key to be constructed */
if (awaken)
@@ -646,7 +645,7 @@ found:
/* this races with key_put(), but that doesn't matter since key_put()
* doesn't actually change the key
*/
- __key_get(key);
+ atomic_inc(&key->usage);
error:
spin_unlock(&key_serial_lock);
@@ -781,27 +780,25 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_perm_t perm,
unsigned long flags)
{
- struct keyring_index_key index_key = {
- .description = description,
- };
+ unsigned long prealloc;
struct key_preparsed_payload prep;
- struct assoc_array_edit *edit;
const struct cred *cred = current_cred();
+ struct key_type *ktype;
struct key *keyring, *key = NULL;
key_ref_t key_ref;
int ret;
/* look up the key type to see if it's one of the registered kernel
* types */
- index_key.type = key_type_lookup(type);
- if (IS_ERR(index_key.type)) {
+ ktype = key_type_lookup(type);
+ if (IS_ERR(ktype)) {
key_ref = ERR_PTR(-ENODEV);
goto error;
}
key_ref = ERR_PTR(-EINVAL);
- if (!index_key.type->match || !index_key.type->instantiate ||
- (!index_key.description && !index_key.type->preparse))
+ if (!ktype->match || !ktype->instantiate ||
+ (!description && !ktype->preparse))
goto error_put_type;
keyring = key_ref_to_ptr(keyring_ref);
@@ -815,28 +812,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
memset(&prep, 0, sizeof(prep));
prep.data = payload;
prep.datalen = plen;
- prep.quotalen = index_key.type->def_datalen;
- prep.trusted = flags & KEY_ALLOC_TRUSTED;
- if (index_key.type->preparse) {
- ret = index_key.type->preparse(&prep);
+ prep.quotalen = ktype->def_datalen;
+ if (ktype->preparse) {
+ ret = ktype->preparse(&prep);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_put_type;
}
- if (!index_key.description)
- index_key.description = prep.description;
+ if (!description)
+ description = prep.description;
key_ref = ERR_PTR(-EINVAL);
- if (!index_key.description)
+ if (!description)
goto error_free_prep;
}
- index_key.desc_len = strlen(index_key.description);
-
- key_ref = ERR_PTR(-EPERM);
- if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
- goto error_free_prep;
- flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
- ret = __key_link_begin(keyring, &index_key, &edit);
+ ret = __key_link_begin(keyring, ktype, description, &prealloc);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_free_prep;
@@ -854,9 +844,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
* key of the same type and description in the destination keyring and
* update that instead if possible
*/
- if (index_key.type->update) {
- key_ref = find_key_to_update(keyring_ref, &index_key);
- if (key_ref)
+ if (ktype->update) {
+ key_ref = __keyring_search_one(keyring_ref, ktype, description,
+ 0);
+ if (!IS_ERR(key_ref))
goto found_matching_key;
}
@@ -865,24 +856,23 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
perm |= KEY_USR_VIEW;
- if (index_key.type->read)
+ if (ktype->read)
perm |= KEY_POS_READ;
- if (index_key.type == &key_type_keyring ||
- index_key.type->update)
+ if (ktype == &key_type_keyring || ktype->update)
perm |= KEY_POS_WRITE;
}
/* allocate a new key */
- key = key_alloc(index_key.type, index_key.description,
- cred->fsuid, cred->fsgid, cred, perm, flags);
+ key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
+ perm, flags);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_link_end;
}
/* instantiate it and link it into the target keyring */
- ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);
+ ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
if (ret < 0) {
key_put(key);
key_ref = ERR_PTR(ret);
@@ -892,12 +882,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_link_end:
- __key_link_end(keyring, &index_key, edit);
+ __key_link_end(keyring, ktype, prealloc);
error_free_prep:
- if (index_key.type->preparse)
- index_key.type->free_preparse(&prep);
+ if (ktype->preparse)
+ ktype->free_preparse(&prep);
error_put_type:
- key_type_put(index_key.type);
+ key_type_put(ktype);
error:
return key_ref;
@@ -905,7 +895,7 @@ error:
/* we found a matching key, so we're going to try to update it
* - we can drop the locks first as we have the key pinned
*/
- __key_link_end(keyring, &index_key, edit);
+ __key_link_end(keyring, ktype, prealloc);
key_ref = __key_update(key_ref, &prep);
goto error_free_prep;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index cee72ce..33cfd27 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1667,9 +1667,6 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_INVALIDATE:
return keyctl_invalidate_key((key_serial_t) arg2);
- case KEYCTL_GET_PERSISTENT:
- return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
-
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 69f0cb7..6ece7f2 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1,6 +1,6 @@
/* Keyring handling
*
- * Copyright (C) 2004-2005, 2008, 2013 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
@@ -17,11 +17,25 @@
#include <linux/seq_file.h>
#include <linux/err.h>
#include <keys/keyring-type.h>
-#include <keys/user-type.h>
-#include <linux/assoc_array_priv.h>
#include <linux/uaccess.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
* set on how deep we're willing to go.
@@ -33,28 +47,6 @@
*/
#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 DEFINE_RWLOCK(keyring_name_lock);
@@ -75,6 +67,7 @@ static inline unsigned keyring_hash(const char *desc)
*/
static int keyring_instantiate(struct key *keyring,
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_destroy(struct key *keyring);
static void keyring_describe(const struct key *keyring, struct seq_file *m);
@@ -83,9 +76,9 @@ static long keyring_read(const struct key *keyring,
struct key_type key_type_keyring = {
.name = "keyring",
- .def_datalen = 0,
+ .def_datalen = sizeof(struct keyring_list),
.instantiate = keyring_instantiate,
- .match = user_match,
+ .match = keyring_match,
.revoke = keyring_revoke,
.destroy = keyring_destroy,
.describe = keyring_describe,
@@ -134,7 +127,6 @@ static int keyring_instantiate(struct key *keyring,
ret = -EINVAL;
if (prep->datalen == 0) {
- assoc_array_init(&keyring->keys);
/* make the keyring available by name if it has one */
keyring_publish_name(keyring);
ret = 0;
@@ -144,226 +136,15 @@ static int keyring_instantiate(struct key *keyring,
}
/*
- * 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.
+ * Match keyrings on their name
*/
-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)
+static int keyring_match(const struct key *keyring, const void *description)
{
- const struct keyring_index_key *index_key = data;
- 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;
+ return keyring->description &&
+ strcmp(keyring->description, description) == 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
* and dispose of its data.
*
@@ -374,6 +155,9 @@ static const struct assoc_array_ops keyring_assoc_array_ops = {
*/
static void keyring_destroy(struct key *keyring)
{
+ struct keyring_list *klist;
+ int loop;
+
if (keyring->description) {
write_lock(&keyring_name_lock);
@@ -384,7 +168,12 @@ static void keyring_destroy(struct key *keyring)
write_unlock(&keyring_name_lock);
}
- assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
+ klist = rcu_access_pointer(keyring->payload.subscriptions);
+ if (klist) {
+ for (loop = klist->nkeys - 1; loop >= 0; loop--)
+ key_put(rcu_access_pointer(klist->keys[loop]));
+ kfree(klist);
+ }
}
/*
@@ -392,88 +181,76 @@ static void keyring_destroy(struct key *keyring)
*/
static void keyring_describe(const struct key *keyring, struct seq_file *m)
{
+ struct keyring_list *klist;
+
if (keyring->description)
seq_puts(m, keyring->description);
else
seq_puts(m, "[anon]");
if (key_is_instantiated(keyring)) {
- if (keyring->keys.nr_leaves_on_tree != 0)
- seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
+ rcu_read_lock();
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (klist)
+ seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
else
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
*
- * 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.
+ * The keyring's semaphore is read-locked by the caller.
*/
static long keyring_read(const struct key *keyring,
char __user *buffer, size_t buflen)
{
- struct keyring_read_iterator_context ctx;
- unsigned long nr_keys;
- int ret;
-
- kenter("{%d},,%zu", key_serial(keyring), buflen);
-
- if (buflen & (sizeof(key_serial_t) - 1))
- return -EINVAL;
-
- nr_keys = keyring->keys.nr_leaves_on_tree;
- if (nr_keys == 0)
- return 0;
-
- /* Calculate how much data we could return */
- ctx.qty = nr_keys * sizeof(key_serial_t);
-
- if (!buffer || !buflen)
- return ctx.qty;
+ struct keyring_list *klist;
+ struct key *key;
+ size_t qty, tmp;
+ int loop, ret;
- if (buflen > ctx.qty)
- ctx.qty = buflen;
+ ret = 0;
+ klist = rcu_dereference_locked_keyring(keyring);
+ if (klist) {
+ /* calculate how much data we could return */
+ qty = klist->nkeys * sizeof(key_serial_t);
+
+ if (buffer && buflen > 0) {
+ if (buflen > qty)
+ 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;
+ }
+ }
- /* 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;
+ ret = qty;
}
- kleave(" = %zu [ok]", ctx.count);
- return ctx.count;
+error:
+ return ret;
}
/*
@@ -500,361 +277,227 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
}
EXPORT_SYMBOL(keyring_alloc);
-/*
- * Iteration function to consider each key found.
+/**
+ * keyring_search_aux - Search a keyring tree for a key matching some criteria
+ * @keyring_ref: A pointer to the keyring with possession indicator.
+ * @cred: The credentials to use for permissions checks.
+ * @type: The type of key to search for.
+ * @description: Parameter for @match.
+ * @match: Function to rule on whether or not a key is the one required.
+ * @no_state_check: Don't check if a matching key is bad
+ *
+ * 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.
*/
-static int keyring_search_iterator(const void *object, void *iterator_data)
+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,
+ bool no_state_check)
{
- struct keyring_search_context *ctx = iterator_data;
- const struct key *key = keyring_ptr_to_key(object);
- unsigned long kflags = key->flags;
+ struct {
+ /* Need a separate keylist pointer for RCU purposes */
+ struct key *keyring;
+ struct keyring_list *keylist;
+ int kix;
+ } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+ struct keyring_list *keylist;
+ struct timespec now;
+ unsigned long possessed, kflags;
+ struct key *keyring, *key;
+ key_ref_t key_ref;
+ long err;
+ int sp, nkeys, kix;
- kenter("{%d}", key->serial);
+ keyring = key_ref_to_ptr(keyring_ref);
+ possessed = is_key_possessed(keyring_ref);
+ key_check(keyring);
- /* ignore keys not of this type */
- if (key->type != ctx->index_key.type) {
- kleave(" = 0 [!type]");
- return 0;
+ /* top keyring must have search permission to begin the search */
+ err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
+ if (err < 0) {
+ key_ref = ERR_PTR(err);
+ goto error;
}
- /* skip invalidated, revoked and expired keys */
- if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
- if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
- (1 << KEY_FLAG_REVOKED))) {
- ctx->result = ERR_PTR(-EKEYREVOKED);
- kleave(" = %d [invrev]", ctx->skipped_ret);
- goto skipped;
- }
+ key_ref = ERR_PTR(-ENOTDIR);
+ if (keyring->type != &key_type_keyring)
+ goto error;
- if (key->expiry && ctx->now.tv_sec >= key->expiry) {
- ctx->result = ERR_PTR(-EKEYEXPIRED);
- kleave(" = %d [expire]", ctx->skipped_ret);
- goto skipped;
- }
- }
+ rcu_read_lock();
- /* keys that don't match */
- if (!ctx->match(key, ctx->match_data)) {
- kleave(" = 0 [!match]");
- return 0;
- }
+ 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;
+ if (no_state_check)
+ goto found;
- /* key must have search permissions */
- if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) &&
- key_task_permission(make_key_ref(key, ctx->possessed),
- ctx->cred, KEY_SEARCH) < 0) {
- ctx->result = ERR_PTR(-EACCES);
- kleave(" = %d [!perm]", ctx->skipped_ret);
- goto skipped;
+ /* 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(key->type_data.reject_error);
+ if (kflags & (1 << KEY_FLAG_NEGATIVE))
+ goto error_2;
+ goto found;
}
- if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
- /* we set a different error code if we pass a negative key */
- if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
- smp_rmb();
- ctx->result = ERR_PTR(key->type_data.reject_error);
- kleave(" = %d [neg]", ctx->skipped_ret);
- goto skipped;
- }
- }
+ /* 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_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED) |
+ (1 << KEY_FLAG_NEGATIVE)) ||
+ (keyring->expiry && 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;
- /* Found */
- ctx->result = make_key_ref(key, ctx->possessed);
- kleave(" = 1 [found]");
- return 1;
+ keylist = rcu_dereference(keyring->payload.subscriptions);
+ if (!keylist)
+ goto not_this_keyring;
-skipped:
- return ctx->skipped_ret;
-}
+ /* iterate through the keys in this keyring first */
+ nkeys = keylist->nkeys;
+ smp_rmb();
+ for (kix = 0; kix < nkeys; kix++) {
+ key = rcu_dereference(keylist->keys[kix]);
+ kflags = key->flags;
-/*
- * Search inside a keyring for a key. We can search by walking to it
- * 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);
-}
+ /* ignore keys not of this type */
+ if (key->type != type)
+ continue;
-/*
- * Search a tree of keyrings that point to other keyrings up to the maximum
- * depth.
- */
-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];
+ /* skip invalidated, revoked and expired keys */
+ if (!no_state_check) {
+ if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
+ continue;
- struct assoc_array_shortcut *shortcut;
- struct assoc_array_node *node;
- struct assoc_array_ptr *ptr;
- struct key *key;
- int sp = 0, slot;
+ if (key->expiry && now.tv_sec >= key->expiry)
+ continue;
+ }
- kenter("{%d},{%s,%s}",
- keyring->serial,
- ctx->index_key.type->name,
- ctx->index_key.description);
+ /* keys that don't match */
+ if (!match(key, description))
+ continue;
- if (ctx->index_key.description)
- ctx->index_key.desc_len = strlen(ctx->index_key.description);
+ /* key must have search permissions */
+ if (key_task_permission(make_key_ref(key, possessed),
+ cred, KEY_SEARCH) < 0)
+ continue;
- /* 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:
+ if (no_state_check)
goto found;
- case 2:
- return false;
- default:
- break;
- }
- }
-
- 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;
+ /* we set a different error code if we pass a negative key */
+ if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
+ err = key->type_data.reject_error;
+ continue;
+ }
- /* Search through the keys in this keyring before its searching its
- * subtrees.
- */
- if (search_keyring(keyring, ctx))
goto found;
-
- /* Then manually iterate through the keyrings nested in this one.
- *
- * Start from the root node of the index tree. Because of the way the
- * hash function has been set up, keyrings cluster on the leftmost
- * branch of the root node (root slot 0) or in the root node itself.
- * Non-keyrings avoid the leftmost branch of the root entirely (root
- * slots 1-15).
- */
- ptr = ACCESS_ONCE(keyring->keys.root);
- if (!ptr)
- goto not_this_keyring;
-
- if (assoc_array_ptr_is_shortcut(ptr)) {
- /* 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.
- */
- 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))
+ /* search through the keyrings nested in this one */
+ kix = 0;
+ascend:
+ nkeys = keylist->nkeys;
+ smp_rmb();
+ for (; kix < nkeys; kix++) {
+ key = rcu_dereference(keylist->keys[kix]);
+ if (key->type != &key_type_keyring)
continue;
- 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;
- }
+ /* recursively search nested keyrings
+ * - only search keyrings for which we have search permission
+ */
+ if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+ continue;
- /* 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)
+ if (key_task_permission(make_key_ref(key, possessed),
+ cred, KEY_SEARCH) < 0)
continue;
/* stack the current position */
stack[sp].keyring = keyring;
- stack[sp].node = node;
- stack[sp].slot = slot;
+ stack[sp].keylist = keylist;
+ stack[sp].kix = kix;
sp++;
/* begin again with the new keyring */
keyring = key;
- goto descend_to_keyring;
+ goto descend;
}
- /* 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
- * matching key.
- */
+ /* the keyring we're looking at was disqualified or didn't contain a
+ * matching key */
not_this_keyring:
- kdebug("not_this_keyring %d", sp);
- if (sp <= 0) {
- kleave(" = false");
- return false;
+ if (sp > 0) {
+ /* resume the processing of a keyring higher up in the tree */
+ sp--;
+ keyring = stack[sp].keyring;
+ keylist = stack[sp].keylist;
+ kix = stack[sp].kix + 1;
+ goto ascend;
}
- /* Resume the processing of a keyring higher up in the tree */
- 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;
+ key_ref = ERR_PTR(err);
+ goto error_2;
- /* We found a viable match */
+ /* we found a viable match */
found:
- key = key_ref_to_ptr(ctx->result);
+ atomic_inc(&key->usage);
+ key->last_used_at = now.tv_sec;
+ keyring->last_used_at = now.tv_sec;
+ while (sp > 0)
+ stack[--sp].keyring->last_used_at = now.tv_sec;
key_check(key);
- if (!(ctx->flags & KEYRING_SEARCH_NO_UPDATE_TIME)) {
- 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));
+ key_ref = make_key_ref(key, possessed);
+error_2:
rcu_read_unlock();
- return ctx->result;
+error:
+ return key_ref;
}
/**
@@ -864,73 +507,77 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
* @description: The name of the keyring we want to find.
*
* As keyring_search_aux() above, but using the current task's credentials and
- * type's default matching function and preferred search method.
+ * type's default matching function.
*/
key_ref_t keyring_search(key_ref_t keyring,
struct key_type *type,
const char *description)
{
- struct keyring_search_context ctx = {
- .index_key.type = type,
- .index_key.description = description,
- .cred = current_cred(),
- .match = type->match,
- .match_data = description,
- .flags = (type->def_lookup_type |
- KEYRING_SEARCH_DO_STATE_CHECK),
- };
-
- if (!ctx.match)
+ if (!type->match)
return ERR_PTR(-ENOKEY);
- return keyring_search_aux(keyring, &ctx);
+ return keyring_search_aux(keyring, current->cred,
+ type, description, type->match, false);
}
EXPORT_SYMBOL(keyring_search);
/*
- * Search the given keyring for a key that might be updated.
+ * Search the given keyring only (no recursion).
*
* The caller must guarantee that the keyring is a keyring and that the
- * permission is granted to modify the keyring as no check is made here. The
- * caller must also hold a lock on the keyring semaphore.
+ * permission is granted to search the keyring as no check is made here.
+ *
+ * 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
- * successful and returns NULL if not found. Revoked and invalidated keys are
- * skipped over.
+ * successful and returns -ENOKEY if not found. Revoked keys and keys not
+ * providing the requested permission are skipped over.
*
* If successful, the possession indicator is propagated from the keyring ref
* to the returned key reference.
*/
-key_ref_t find_key_to_update(key_ref_t keyring_ref,
- const struct keyring_index_key *index_key)
+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;
+ unsigned long possessed;
struct key *keyring, *key;
- const void *object;
+ int nkeys, loop;
keyring = key_ref_to_ptr(keyring_ref);
+ possessed = is_key_possessed(keyring_ref);
- kenter("{%d},{%s,%s}",
- keyring->serial, index_key->type->name, index_key->description);
-
- object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops,
- index_key);
+ rcu_read_lock();
- if (object)
- goto found;
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (klist) {
+ nkeys = klist->nkeys;
+ smp_rmb();
+ for (loop = 0; loop < nkeys ; loop++) {
+ key = rcu_dereference(klist->keys[loop]);
+ if (key->type == ktype &&
+ (!key->type->match ||
+ key->type->match(key, description)) &&
+ key_permission(make_key_ref(key, possessed),
+ perm) == 0 &&
+ !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
+ )
+ goto found;
+ }
+ }
- kleave(" = NULL");
- return NULL;
+ rcu_read_unlock();
+ return ERR_PTR(-ENOKEY);
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);
- kleave(" = {%d}", key->serial);
- return make_key_ref(key, is_key_possessed(keyring_ref));
+ atomic_inc(&key->usage);
+ keyring->last_used_at = key->last_used_at =
+ current_kernel_time().tv_sec;
+ rcu_read_unlock();
+ return make_key_ref(key, possessed);
}
/*
@@ -993,19 +640,6 @@ out:
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
* tree A at the topmost level (ie: as a direct child of A).
@@ -1015,39 +649,116 @@ static int keyring_detect_cycle_iterator(const void *object,
*/
static int keyring_detect_cycle(struct key *A, struct key *B)
{
- struct keyring_search_context ctx = {
- .index_key = A->index_key,
- .match_data = A,
- .iterator = keyring_detect_cycle_iterator,
- .flags = (KEYRING_SEARCH_LOOKUP_DIRECT |
- KEYRING_SEARCH_NO_STATE_CHECK |
- KEYRING_SEARCH_NO_UPDATE_TIME |
- KEYRING_SEARCH_NO_CHECK_PERM |
- KEYRING_SEARCH_DETECT_TOO_DEEP),
- };
+ struct {
+ struct keyring_list *keylist;
+ int kix;
+ } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+ struct keyring_list *keylist;
+ struct key *subtree, *key;
+ int sp, nkeys, kix, ret;
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();
- return PTR_ERR(ctx.result) == -EAGAIN ? 0 : PTR_ERR(ctx.result);
+ return ret;
+
+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.
*/
-int __key_link_begin(struct key *keyring,
- const struct keyring_index_key *index_key,
- struct assoc_array_edit **_edit)
+int __key_link_begin(struct key *keyring, const struct key_type *type,
+ const char *description, unsigned long *_prealloc)
__acquires(&keyring->sem)
__acquires(&keyring_serialise_link_sem)
{
- struct assoc_array_edit *edit;
- int ret;
-
- kenter("%d,%s,%s,",
- keyring->serial, index_key->type->name, index_key->description);
+ struct keyring_list *klist, *nklist;
+ unsigned long prealloc;
+ unsigned max;
+ time_t lowest_lru;
+ size_t size;
+ int loop, lru, ret;
- BUG_ON(index_key->desc_len == 0);
+ kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
if (keyring->type != &key_type_keyring)
return -ENOTDIR;
@@ -1060,39 +771,100 @@ int __key_link_begin(struct key *keyring,
/* serialise link/link calls to prevent parallel calls causing a cycle
* when linking two keyring in opposite orders */
- if (index_key->type == &key_type_keyring)
+ if (type == &key_type_keyring)
down_write(&keyring_serialise_link_sem);
- /* Create an edit script that will insert/replace the key in the
- * keyring tree.
- */
- edit = assoc_array_insert(&keyring->keys,
- &keyring_assoc_array_ops,
- index_key,
- NULL);
- if (IS_ERR(edit)) {
- ret = PTR_ERR(edit);
- goto error_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 == type &&
+ strcmp(key->description, 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 we're not replacing a link in-place then we're going to need some
- * extra quota.
- */
- if (!edit->dead_leaf) {
- ret = key_payload_reserve(keyring,
- keyring->datalen + KEYQUOTA_LINK_BYTES);
- if (ret < 0)
- goto error_cancel;
+ /* 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 */
+ ret = key_payload_reserve(keyring,
+ keyring->datalen + KEYQUOTA_LINK_BYTES);
+ if (ret < 0)
+ goto error_sem;
+
+ if (klist && klist->nkeys < klist->maxkeys) {
+ /* there's sufficient slack space to append directly */
+ klist->delkey = klist->nkeys;
+ prealloc = KEY_LINK_FIXQUOTA;
+ } else {
+ /* grow the key list */
+ max = 4;
+ if (klist) {
+ max += klist->maxkeys;
+ if (max > MAX_KEYRING_LINKS)
+ 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;
}
- *_edit = edit;
+done:
+ *_prealloc = prealloc;
kleave(" = 0");
return 0;
-error_cancel:
- assoc_array_cancel_edit(edit);
+error_quota:
+ /* undo the quota changes */
+ key_payload_reserve(keyring,
+ keyring->datalen - KEYQUOTA_LINK_BYTES);
error_sem:
- if (index_key->type == &key_type_keyring)
+ if (type == &key_type_keyring)
up_write(&keyring_serialise_link_sem);
error_krsem:
up_write(&keyring->sem);
@@ -1123,12 +895,60 @@ 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
* combination.
*/
-void __key_link(struct key *key, struct assoc_array_edit **_edit)
+void __key_link(struct key *keyring, struct key *key,
+ unsigned long *_prealloc)
{
- __key_get(key);
- assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key));
- assoc_array_apply_edit(*_edit);
- *_edit = NULL;
+ 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);
+
+ atomic_inc(&key->usage);
+ keyring->last_used_at = key->last_used_at =
+ current_kernel_time().tv_sec;
+
+ /* 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++;
+ }
}
/*
@@ -1136,22 +956,24 @@ void __key_link(struct key *key, struct assoc_array_edit **_edit)
*
* Must be called with __key_link_begin() having being called.
*/
-void __key_link_end(struct key *keyring,
- const struct keyring_index_key *index_key,
- struct assoc_array_edit *edit)
+void __key_link_end(struct key *keyring, struct key_type *type,
+ unsigned long prealloc)
__releases(&keyring->sem)
__releases(&keyring_serialise_link_sem)
{
- BUG_ON(index_key->type == NULL);
- kenter("%d,%s,", keyring->serial, index_key->type->name);
+ BUG_ON(type == NULL);
+ BUG_ON(type->name == NULL);
+ kenter("%d,%s,%lx", keyring->serial, type->name, prealloc);
- if (index_key->type == &key_type_keyring)
+ if (type == &key_type_keyring)
up_write(&keyring_serialise_link_sem);
- if (edit && !edit->dead_leaf) {
- key_payload_reserve(keyring,
- keyring->datalen - KEYQUOTA_LINK_BYTES);
- assoc_array_cancel_edit(edit);
+ if (prealloc) {
+ if (prealloc & KEY_LINK_FIXQUOTA)
+ key_payload_reserve(keyring,
+ keyring->datalen -
+ KEYQUOTA_LINK_BYTES);
+ kfree((struct keyring_list *)(prealloc & ~KEY_LINK_FIXQUOTA));
}
up_write(&keyring->sem);
}
@@ -1178,28 +1000,20 @@ void __key_link_end(struct key *keyring,
*/
int key_link(struct key *keyring, struct key *key)
{
- struct assoc_array_edit *edit;
+ unsigned long prealloc;
int ret;
- kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage));
-
key_check(keyring);
key_check(key);
- if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
- !test_bit(KEY_FLAG_TRUSTED, &key->flags))
- return -EPERM;
-
- ret = __key_link_begin(keyring, &key->index_key, &edit);
+ ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
if (ret == 0) {
- kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
ret = __key_link_check_live_key(keyring, key);
if (ret == 0)
- __key_link(key, &edit);
- __key_link_end(keyring, &key->index_key, edit);
+ __key_link(keyring, key, &prealloc);
+ __key_link_end(keyring, key->type, prealloc);
}
- kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));
return ret;
}
EXPORT_SYMBOL(key_link);
@@ -1223,37 +1037,90 @@ EXPORT_SYMBOL(key_link);
*/
int key_unlink(struct key *keyring, struct key *key)
{
- struct assoc_array_edit *edit;
- int ret;
+ struct keyring_list *klist, *nklist;
+ int loop, ret;
key_check(keyring);
key_check(key);
+ ret = -ENOTDIR;
if (keyring->type != &key_type_keyring)
- return -ENOTDIR;
+ goto error;
down_write(&keyring->sem);
- edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops,
- &key->index_key);
- if (IS_ERR(edit)) {
- ret = PTR_ERR(edit);
- goto error;
+ klist = rcu_dereference_locked_keyring(keyring);
+ if (klist) {
+ /* search the keyring for the key */
+ for (loop = 0; loop < klist->nkeys; loop++)
+ if (rcu_access_pointer(klist->keys[loop]) == key)
+ goto key_is_present;
}
+
+ up_write(&keyring->sem);
ret = -ENOENT;
- if (edit == NULL)
- goto error;
+ 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);
- key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);
ret = 0;
error:
- up_write(&keyring->sem);
return ret;
+nomem:
+ ret = -ENOMEM;
+ up_write(&keyring->sem);
+ goto error;
}
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: The keyring to clear.
@@ -1264,25 +1131,33 @@ EXPORT_SYMBOL(key_unlink);
*/
int keyring_clear(struct key *keyring)
{
- struct assoc_array_edit *edit;
+ struct keyring_list *klist;
int ret;
- if (keyring->type != &key_type_keyring)
- return -ENOTDIR;
+ ret = -ENOTDIR;
+ if (keyring->type == &key_type_keyring) {
+ /* detach the pointer block with the locks held */
+ down_write(&keyring->sem);
- 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 */
+ 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;
}
- up_write(&keyring->sem);
return ret;
}
EXPORT_SYMBOL(keyring_clear);
@@ -1294,68 +1169,111 @@ EXPORT_SYMBOL(keyring_clear);
*/
static void keyring_revoke(struct key *keyring)
{
- struct assoc_array_edit *edit;
-
- 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);
- }
-}
-
-static bool keyring_gc_select_iterator(void *object, void *iterator_data)
-{
- struct key *key = keyring_ptr_to_key(object);
- time_t *limit = iterator_data;
+ struct keyring_list *klist;
- if (key_is_dead(key, *limit))
- return false;
- key_get(key);
- return true;
-}
+ klist = rcu_dereference_locked_keyring(keyring);
-static int keyring_gc_check_iterator(const void *object, void *iterator_data)
-{
- const struct key *key = keyring_ptr_to_key(object);
- time_t *limit = iterator_data;
+ /* adjust the quota */
+ key_payload_reserve(keyring, 0);
- key_check(key);
- return key_is_dead(key, *limit);
+ if (klist) {
+ rcu_assign_pointer(keyring->payload.subscriptions, NULL);
+ call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+ }
}
/*
- * Garbage collect pointers from a keyring.
+ * Collect garbage from the contents of a keyring, replacing the old list with
+ * a new one with the pointers all shuffled down.
*
- * Not called with any locks held. The keyring's key struct will not be
- * deallocated under us as only our caller may deallocate it.
+ * Dead keys are classed as oned that are flagged as being dead or are revoked,
+ * expired or negative keys that were revoked or expired before the specified
+ * limit.
*/
void keyring_gc(struct key *keyring, time_t limit)
{
- int result;
+ struct keyring_list *klist, *new;
+ struct key *key;
+ int loop, keep, max;
- kenter("%x{%s}", keyring->serial, keyring->description ?: "");
+ kenter("{%x,%s}", key_serial(keyring), keyring->description);
- if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
- (1 << KEY_FLAG_REVOKED)))
- goto dont_gc;
+ down_write(&keyring->sem);
- /* scan the keyring looking for dead keys */
- rcu_read_lock();
- result = assoc_array_iterate(&keyring->keys,
- keyring_gc_check_iterator, &limit);
- rcu_read_unlock();
- if (result == true)
- goto do_gc;
+ klist = rcu_dereference_locked_keyring(keyring);
+ 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);
+ }
-dont_gc:
- kleave(" [no gc]");
+ up_write(&keyring->sem);
+
+ call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+ kleave(" [yes]");
return;
-do_gc:
- down_write(&keyring->sem);
- assoc_array_gc(&keyring->keys, &keyring_assoc_array_ops,
- keyring_gc_select_iterator, &limit);
+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(" [gc]");
+ kleave(" [oom]");
}
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
deleted file mode 100644
index 0ad3ee2..0000000
--- a/security/keys/persistent.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* General persistent per-UID keyrings register
- *
- * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
- */
-
-#include <linux/user_namespace.h>
-#include "internal.h"
-
-unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
-
-/*
- * Create the persistent keyring register for the current user namespace.
- *
- * Called with the namespace's sem locked for writing.
- */
-static int key_create_persistent_register(struct user_namespace *ns)
-{
- struct key *reg = keyring_alloc(".persistent_register",
- KUIDT_INIT(0), KGIDT_INIT(0),
- current_cred(),
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
- if (IS_ERR(reg))
- return PTR_ERR(reg);
-
- ns->persistent_keyring_register = reg;
- return 0;
-}
-
-/*
- * Create the persistent keyring for the specified user.
- *
- * Called with the namespace's sem locked for writing.
- */
-static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
- struct keyring_index_key *index_key)
-{
- struct key *persistent;
- key_ref_t reg_ref, persistent_ref;
-
- if (!ns->persistent_keyring_register) {
- long err = key_create_persistent_register(ns);
- if (err < 0)
- return ERR_PTR(err);
- } else {
- reg_ref = make_key_ref(ns->persistent_keyring_register, true);
- persistent_ref = find_key_to_update(reg_ref, index_key);
- if (persistent_ref)
- return persistent_ref;
- }
-
- persistent = keyring_alloc(index_key->description,
- uid, INVALID_GID, current_cred(),
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA,
- ns->persistent_keyring_register);
- if (IS_ERR(persistent))
- return ERR_CAST(persistent);
-
- return make_key_ref(persistent, true);
-}
-
-/*
- * Get the persistent keyring for a specific UID and link it to the nominated
- * keyring.
- */
-static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
- key_ref_t dest_ref)
-{
- struct keyring_index_key index_key;
- struct key *persistent;
- key_ref_t reg_ref, persistent_ref;
- char buf[32];
- long ret;
-
- /* Look in the register if it exists */
- index_key.type = &key_type_keyring;
- index_key.description = buf;
- index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
-
- if (ns->persistent_keyring_register) {
- reg_ref = make_key_ref(ns->persistent_keyring_register, true);
- down_read(&ns->persistent_keyring_register_sem);
- persistent_ref = find_key_to_update(reg_ref, &index_key);
- up_read(&ns->persistent_keyring_register_sem);
-
- if (persistent_ref)
- goto found;
- }
-
- /* It wasn't in the register, so we'll need to create it. We might
- * also need to create the register.
- */
- down_write(&ns->persistent_keyring_register_sem);
- persistent_ref = key_create_persistent(ns, uid, &index_key);
- up_write(&ns->persistent_keyring_register_sem);
- if (!IS_ERR(persistent_ref))
- goto found;
-
- return PTR_ERR(persistent_ref);
-
-found:
- ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK);
- if (ret == 0) {
- persistent = key_ref_to_ptr(persistent_ref);
- ret = key_link(key_ref_to_ptr(dest_ref), persistent);
- if (ret == 0) {
- key_set_timeout(persistent, persistent_keyring_expiry);
- ret = persistent->serial;
- }
- }
-
- key_ref_put(persistent_ref);
- return ret;
-}
-
-/*
- * Get the persistent keyring for a specific UID and link it to the nominated
- * keyring.
- */
-long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
-{
- struct user_namespace *ns = current_user_ns();
- key_ref_t dest_ref;
- kuid_t uid;
- long ret;
-
- /* -1 indicates the current user */
- if (_uid == (uid_t)-1) {
- uid = current_uid();
- } else {
- uid = make_kuid(ns, _uid);
- if (!uid_valid(uid))
- return -EINVAL;
-
- /* You can only see your own persistent cache if you're not
- * sufficiently privileged.
- */
- if (!uid_eq(uid, current_uid()) &&
- !uid_eq(uid, current_euid()) &&
- !ns_capable(ns, CAP_SETUID))
- return -EPERM;
- }
-
- /* There must be a destination keyring */
- dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE);
- if (IS_ERR(dest_ref))
- return PTR_ERR(dest_ref);
- if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
- ret = -ENOTDIR;
- goto out_put_dest;
- }
-
- ret = key_get_persistent(ns, uid, dest_ref);
-
-out_put_dest:
- key_ref_put(dest_ref);
- return ret;
-}
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 88e9a46..217b685 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -182,6 +182,7 @@ static void proc_keys_stop(struct seq_file *p, void *v)
static int proc_keys_show(struct seq_file *m, void *v)
{
+ const struct cred *cred = current_cred();
struct rb_node *_p = v;
struct key *key = rb_entry(_p, struct key, serial_node);
struct timespec now;
@@ -190,23 +191,15 @@ static int proc_keys_show(struct seq_file *m, void *v)
char xbuf[12];
int rc;
- struct keyring_search_context ctx = {
- .index_key.type = key->type,
- .index_key.description = key->description,
- .cred = current_cred(),
- .match = lookup_user_key_possessed,
- .match_data = key,
- .flags = (KEYRING_SEARCH_NO_STATE_CHECK |
- KEYRING_SEARCH_LOOKUP_DIRECT),
- };
-
key_ref = make_key_ref(key, 0);
/* determine if the key is possessed by this process (a test we can
* skip if the key does not indicate the possessor can view it
*/
if (key->perm & KEY_POS_VIEW) {
- skey_ref = search_my_process_keyrings(&ctx);
+ skey_ref = search_my_process_keyrings(key->type, key,
+ lookup_user_key_possessed,
+ true, cred);
if (!IS_ERR(skey_ref)) {
key_ref_put(skey_ref);
key_ref = make_key_ref(key, 1);
@@ -218,7 +211,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
* - the caller holds a spinlock, and thus the RCU read lock, making our
* access to __current_cred() safe
*/
- rc = key_task_permission(key_ref, ctx.cred, KEY_VIEW);
+ rc = key_task_permission(key_ref, cred, KEY_VIEW);
if (rc < 0)
return 0;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 0cf8a13..42defae 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -235,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
if (IS_ERR(keyring))
return PTR_ERR(keyring);
} else {
- __key_get(keyring);
+ atomic_inc(&keyring->usage);
}
/* install the keyring */
@@ -319,7 +319,11 @@ void key_fsgid_changed(struct task_struct *tsk)
* In the case of a successful return, the possession attribute is set on the
* returned key reference.
*/
-key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
+key_ref_t search_my_process_keyrings(struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ bool no_state_check,
+ const struct cred *cred)
{
key_ref_t key_ref, ret, err;
@@ -335,9 +339,10 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
err = ERR_PTR(-EAGAIN);
/* search the thread keyring first */
- if (ctx->cred->thread_keyring) {
+ if (cred->thread_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(ctx->cred->thread_keyring, 1), ctx);
+ make_key_ref(cred->thread_keyring, 1),
+ cred, type, description, match, no_state_check);
if (!IS_ERR(key_ref))
goto found;
@@ -353,9 +358,10 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
}
/* search the process keyring second */
- if (ctx->cred->process_keyring) {
+ if (cred->process_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(ctx->cred->process_keyring, 1), ctx);
+ make_key_ref(cred->process_keyring, 1),
+ cred, type, description, match, no_state_check);
if (!IS_ERR(key_ref))
goto found;
@@ -373,11 +379,11 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
}
/* search the session keyring */
- if (ctx->cred->session_keyring) {
+ if (cred->session_keyring) {
rcu_read_lock();
key_ref = keyring_search_aux(
- make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1),
- ctx);
+ make_key_ref(rcu_dereference(cred->session_keyring), 1),
+ cred, type, description, match, no_state_check);
rcu_read_unlock();
if (!IS_ERR(key_ref))
@@ -396,10 +402,10 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
}
}
/* or search the user-session keyring */
- else if (ctx->cred->user->session_keyring) {
+ else if (cred->user->session_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(ctx->cred->user->session_keyring, 1),
- ctx);
+ make_key_ref(cred->user->session_keyring, 1),
+ cred, type, description, match, no_state_check);
if (!IS_ERR(key_ref))
goto found;
@@ -431,14 +437,18 @@ found:
*
* Return same as search_my_process_keyrings().
*/
-key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
+key_ref_t search_process_keyrings(struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ const struct cred *cred)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
might_sleep();
- key_ref = search_my_process_keyrings(ctx);
+ key_ref = search_my_process_keyrings(type, description, match,
+ false, cred);
if (!IS_ERR(key_ref))
goto found;
err = key_ref;
@@ -447,21 +457,18 @@ key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
* search the keyrings of the process mentioned there
* - we don't permit access to request_key auth keys via this method
*/
- if (ctx->cred->request_key_auth &&
- ctx->cred == current_cred() &&
- ctx->index_key.type != &key_type_request_key_auth
+ if (cred->request_key_auth &&
+ cred == current_cred() &&
+ type != &key_type_request_key_auth
) {
- const struct cred *cred = ctx->cred;
-
/* defend against the auth key being revoked */
down_read(&cred->request_key_auth->sem);
- if (key_validate(ctx->cred->request_key_auth) == 0) {
- rka = ctx->cred->request_key_auth->payload.data;
+ if (key_validate(cred->request_key_auth) == 0) {
+ rka = cred->request_key_auth->payload.data;
- ctx->cred = rka->cred;
- key_ref = search_process_keyrings(ctx);
- ctx->cred = cred;
+ key_ref = search_process_keyrings(type, description,
+ match, rka->cred);
up_read(&cred->request_key_auth->sem);
@@ -515,23 +522,19 @@ int lookup_user_key_possessed(const struct key *key, const void *target)
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_perm_t perm)
{
- struct keyring_search_context ctx = {
- .match = lookup_user_key_possessed,
- .flags = (KEYRING_SEARCH_NO_STATE_CHECK |
- KEYRING_SEARCH_LOOKUP_DIRECT),
- };
struct request_key_auth *rka;
+ const struct cred *cred;
struct key *key;
key_ref_t key_ref, skey_ref;
int ret;
try_again:
- ctx.cred = get_current_cred();
+ cred = get_current_cred();
key_ref = ERR_PTR(-ENOKEY);
switch (id) {
case KEY_SPEC_THREAD_KEYRING:
- if (!ctx.cred->thread_keyring) {
+ if (!cred->thread_keyring) {
if (!(lflags & KEY_LOOKUP_CREATE))
goto error;
@@ -543,13 +546,13 @@ try_again:
goto reget_creds;
}
- key = ctx.cred->thread_keyring;
- __key_get(key);
+ key = cred->thread_keyring;
+ atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_PROCESS_KEYRING:
- if (!ctx.cred->process_keyring) {
+ if (!cred->process_keyring) {
if (!(lflags & KEY_LOOKUP_CREATE))
goto error;
@@ -561,13 +564,13 @@ try_again:
goto reget_creds;
}
- key = ctx.cred->process_keyring;
- __key_get(key);
+ key = cred->process_keyring;
+ atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_SESSION_KEYRING:
- if (!ctx.cred->session_keyring) {
+ if (!cred->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
ret = install_user_keyrings();
@@ -577,13 +580,13 @@ try_again:
ret = join_session_keyring(NULL);
else
ret = install_session_keyring(
- ctx.cred->user->session_keyring);
+ cred->user->session_keyring);
if (ret < 0)
goto error;
goto reget_creds;
- } else if (ctx.cred->session_keyring ==
- ctx.cred->user->session_keyring &&
+ } else if (cred->session_keyring ==
+ cred->user->session_keyring &&
lflags & KEY_LOOKUP_CREATE) {
ret = join_session_keyring(NULL);
if (ret < 0)
@@ -592,33 +595,33 @@ try_again:
}
rcu_read_lock();
- key = rcu_dereference(ctx.cred->session_keyring);
- __key_get(key);
+ key = rcu_dereference(cred->session_keyring);
+ atomic_inc(&key->usage);
rcu_read_unlock();
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_KEYRING:
- if (!ctx.cred->user->uid_keyring) {
+ if (!cred->user->uid_keyring) {
ret = install_user_keyrings();
if (ret < 0)
goto error;
}
- key = ctx.cred->user->uid_keyring;
- __key_get(key);
+ key = cred->user->uid_keyring;
+ atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_SESSION_KEYRING:
- if (!ctx.cred->user->session_keyring) {
+ if (!cred->user->session_keyring) {
ret = install_user_keyrings();
if (ret < 0)
goto error;
}
- key = ctx.cred->user->session_keyring;
- __key_get(key);
+ key = cred->user->session_keyring;
+ atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
@@ -628,29 +631,29 @@ try_again:
goto error;
case KEY_SPEC_REQKEY_AUTH_KEY:
- key = ctx.cred->request_key_auth;
+ key = cred->request_key_auth;
if (!key)
goto error;
- __key_get(key);
+ atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_REQUESTOR_KEYRING:
- if (!ctx.cred->request_key_auth)
+ if (!cred->request_key_auth)
goto error;
- down_read(&ctx.cred->request_key_auth->sem);
+ down_read(&cred->request_key_auth->sem);
if (test_bit(KEY_FLAG_REVOKED,
- &ctx.cred->request_key_auth->flags)) {
+ &cred->request_key_auth->flags)) {
key_ref = ERR_PTR(-EKEYREVOKED);
key = NULL;
} else {
- rka = ctx.cred->request_key_auth->payload.data;
+ rka = cred->request_key_auth->payload.data;
key = rka->dest_keyring;
- __key_get(key);
+ atomic_inc(&key->usage);
}
- up_read(&ctx.cred->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
if (!key)
goto error;
key_ref = make_key_ref(key, 1);
@@ -670,13 +673,9 @@ try_again:
key_ref = make_key_ref(key, 0);
/* check to see if we possess the key */
- ctx.index_key.type = key->type;
- ctx.index_key.description = key->description;
- ctx.index_key.desc_len = strlen(key->description);
- ctx.match_data = key;
- kdebug("check possessed");
- skey_ref = search_process_keyrings(&ctx);
- kdebug("possessed=%p", skey_ref);
+ skey_ref = search_process_keyrings(key->type, key,
+ lookup_user_key_possessed,
+ cred);
if (!IS_ERR(skey_ref)) {
key_put(key);
@@ -716,14 +715,14 @@ try_again:
goto invalid_key;
/* check the permissions */
- ret = key_task_permission(key_ref, ctx.cred, perm);
+ ret = key_task_permission(key_ref, cred, perm);
if (ret < 0)
goto invalid_key;
key->last_used_at = current_kernel_time().tv_sec;
error:
- put_cred(ctx.cred);
+ put_cred(cred);
return key_ref;
invalid_key:
@@ -734,7 +733,7 @@ invalid_key:
/* if we attempted to install a keyring, then it may have caused new
* creds to be installed */
reget_creds:
- put_cred(ctx.cred);
+ put_cred(cred);
goto try_again;
}
@@ -857,13 +856,3 @@ void key_change_session_keyring(struct callback_head *twork)
commit_creds(new);
}
-
-/*
- * Make sure that root's user and user-session keyrings exist.
- */
-static int __init init_root_keyring(void)
-{
- return install_user_keyrings();
-}
-
-late_initcall(init_root_keyring);
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 3814119..c411f9b 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -345,34 +345,33 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)
* May return a key that's already under construction instead if there was a
* race between two thread calling request_key().
*/
-static int construct_alloc_key(struct keyring_search_context *ctx,
+static int construct_alloc_key(struct key_type *type,
+ const char *description,
struct key *dest_keyring,
unsigned long flags,
struct key_user *user,
struct key **_key)
{
- struct assoc_array_edit *edit;
+ const struct cred *cred = current_cred();
+ unsigned long prealloc;
struct key *key;
key_perm_t perm;
key_ref_t key_ref;
int ret;
- kenter("%s,%s,,,",
- ctx->index_key.type->name, ctx->index_key.description);
+ kenter("%s,%s,,,", type->name, description);
*_key = NULL;
mutex_lock(&user->cons_lock);
perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
perm |= KEY_USR_VIEW;
- if (ctx->index_key.type->read)
+ if (type->read)
perm |= KEY_POS_READ;
- if (ctx->index_key.type == &key_type_keyring ||
- ctx->index_key.type->update)
+ if (type == &key_type_keyring || type->update)
perm |= KEY_POS_WRITE;
- key = key_alloc(ctx->index_key.type, ctx->index_key.description,
- ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
+ key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
perm, flags);
if (IS_ERR(key))
goto alloc_failed;
@@ -380,7 +379,8 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) {
- ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
+ ret = __key_link_begin(dest_keyring, type, description,
+ &prealloc);
if (ret < 0)
goto link_prealloc_failed;
}
@@ -390,16 +390,16 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
* waited for locks */
mutex_lock(&key_construction_mutex);
- key_ref = search_process_keyrings(ctx);
+ key_ref = search_process_keyrings(type, description, type->match, cred);
if (!IS_ERR(key_ref))
goto key_already_present;
if (dest_keyring)
- __key_link(key, &edit);
+ __key_link(dest_keyring, key, &prealloc);
mutex_unlock(&key_construction_mutex);
if (dest_keyring)
- __key_link_end(dest_keyring, &ctx->index_key, edit);
+ __key_link_end(dest_keyring, type, prealloc);
mutex_unlock(&user->cons_lock);
*_key = key;
kleave(" = 0 [%d]", key_serial(key));
@@ -414,8 +414,8 @@ key_already_present:
if (dest_keyring) {
ret = __key_link_check_live_key(dest_keyring, key);
if (ret == 0)
- __key_link(key, &edit);
- __key_link_end(dest_keyring, &ctx->index_key, edit);
+ __key_link(dest_keyring, key, &prealloc);
+ __key_link_end(dest_keyring, type, prealloc);
if (ret < 0)
goto link_check_failed;
}
@@ -444,7 +444,8 @@ alloc_failed:
/*
* Commence key construction.
*/
-static struct key *construct_key_and_link(struct keyring_search_context *ctx,
+static struct key *construct_key_and_link(struct key_type *type,
+ const char *description,
const char *callout_info,
size_t callout_len,
void *aux,
@@ -463,7 +464,8 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
construct_get_dest_keyring(&dest_keyring);
- ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);
+ ret = construct_alloc_key(type, description, dest_keyring, flags, user,
+ &key);
key_user_put(user);
if (ret == 0) {
@@ -527,24 +529,17 @@ struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags)
{
- struct keyring_search_context ctx = {
- .index_key.type = type,
- .index_key.description = description,
- .cred = current_cred(),
- .match = type->match,
- .match_data = description,
- .flags = KEYRING_SEARCH_LOOKUP_DIRECT,
- };
+ const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
int ret;
kenter("%s,%s,%p,%zu,%p,%p,%lx",
- ctx.index_key.type->name, ctx.index_key.description,
- callout_info, callout_len, aux, dest_keyring, flags);
+ type->name, description, callout_info, callout_len, aux,
+ dest_keyring, flags);
/* search all the process keyrings for a key */
- key_ref = search_process_keyrings(&ctx);
+ key_ref = search_process_keyrings(type, description, type->match, cred);
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
@@ -567,8 +562,9 @@ struct key *request_key_and_link(struct key_type *type,
if (!callout_info)
goto error;
- key = construct_key_and_link(&ctx, callout_info, callout_len,
- aux, dest_keyring, flags);
+ key = construct_key_and_link(type, description, callout_info,
+ callout_len, aux, dest_keyring,
+ flags);
}
error:
@@ -596,10 +592,8 @@ int wait_for_key_construction(struct key *key, bool intr)
intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
if (ret < 0)
return ret;
- if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
- smp_rmb();
+ if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
return key->type_data.reject_error;
- }
return key_validate(key);
}
EXPORT_SYMBOL(wait_for_key_construction);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 7495a93..85730d5 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <asm/uaccess.h>
#include "internal.h"
-#include <keys/user-type.h>
static int request_key_auth_instantiate(struct key *,
struct key_preparsed_payload *);
@@ -223,26 +222,32 @@ error_alloc:
}
/*
+ * See if an authorisation key is associated with a particular key.
+ */
+static int key_get_instantiation_authkey_match(const struct key *key,
+ const void *_id)
+{
+ struct request_key_auth *rka = key->payload.data;
+ key_serial_t id = (key_serial_t)(unsigned long) _id;
+
+ return rka->target_key->serial == id;
+}
+
+/*
* Search the current process's keyrings for the authorisation key for
* instantiation of a key.
*/
struct key *key_get_instantiation_authkey(key_serial_t target_id)
{
- char description[16];
- struct keyring_search_context ctx = {
- .index_key.type = &key_type_request_key_auth,
- .index_key.description = description,
- .cred = current_cred(),
- .match = user_match,
- .match_data = description,
- .flags = KEYRING_SEARCH_LOOKUP_DIRECT,
- };
+ const struct cred *cred = current_cred();
struct key *authkey;
key_ref_t authkey_ref;
- sprintf(description, "%x", target_id);
-
- authkey_ref = search_process_keyrings(&ctx);
+ authkey_ref = search_process_keyrings(
+ &key_type_request_key_auth,
+ (void *) (unsigned long) target_id,
+ key_get_instantiation_authkey_match,
+ cred);
if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index 8c0af08..ee32d18 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -61,16 +61,5 @@ ctl_table key_sysctls[] = {
.extra1 = (void *) &zero,
.extra2 = (void *) &max,
},
-#ifdef CONFIG_PERSISTENT_KEYRINGS
- {
- .procname = "persistent_keyring_expiry",
- .data = &persistent_keyring_expiry,
- .maxlen = sizeof(unsigned),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &zero,
- .extra2 = (void *) &max,
- },
-#endif
{ }
};
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index faa2cae..55dc889 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -25,15 +25,14 @@ static int logon_vet_description(const char *desc);
* arbitrary blob of data as the payload
*/
struct key_type key_type_user = {
- .name = "user",
- .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
- .instantiate = user_instantiate,
- .update = user_update,
- .match = user_match,
- .revoke = user_revoke,
- .destroy = user_destroy,
- .describe = user_describe,
- .read = user_read,
+ .name = "user",
+ .instantiate = user_instantiate,
+ .update = user_update,
+ .match = user_match,
+ .revoke = user_revoke,
+ .destroy = user_destroy,
+ .describe = user_describe,
+ .read = user_read,
};
EXPORT_SYMBOL_GPL(key_type_user);
@@ -46,7 +45,6 @@ EXPORT_SYMBOL_GPL(key_type_user);
*/
struct key_type key_type_logon = {
.name = "logon",
- .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.instantiate = user_instantiate,
.update = user_update,
.match = user_match,