diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/Kconfig | 29 | ||||
-rw-r--r-- | security/keys/Makefile | 2 | ||||
-rw-r--r-- | security/keys/big_key.c | 207 | ||||
-rw-r--r-- | security/keys/compat.c | 3 | ||||
-rw-r--r-- | security/keys/gc.c | 47 | ||||
-rw-r--r-- | security/keys/internal.h | 74 | ||||
-rw-r--r-- | security/keys/key.c | 102 | ||||
-rw-r--r-- | security/keys/keyctl.c | 3 | ||||
-rw-r--r-- | security/keys/keyring.c | 1536 | ||||
-rw-r--r-- | security/keys/persistent.c | 167 | ||||
-rw-r--r-- | security/keys/proc.c | 17 | ||||
-rw-r--r-- | security/keys/process_keys.c | 141 | ||||
-rw-r--r-- | security/keys/request_key.c | 60 | ||||
-rw-r--r-- | security/keys/request_key_auth.c | 31 | ||||
-rw-r--r-- | security/keys/sysctl.c | 11 | ||||
-rw-r--r-- | security/keys/user_defined.c | 18 |
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, |