Message ID | 20130801173854.28023.82045.stgit@warthog.procyon.org.uk (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
I think this is the wrong design. There are two problems you're trying to solve: a) how rpc.gssd finds credentials for processes on behalf of which it's acting b) how to create tmpfs locations in which to store credentials (which can be unbounded in size, so storing them in the kernel is silly; also the apps that use them are all user-land apps, so that too means there's no point in storing them in the kernel). Keyrings for the forst problem (or PAGs, or anything similar) is fine: on upcall to gssd the kernel tells it what it needs to know to find those credentials. (b) can be solved in many ways, and the simplest is to have a filesystem where top-level directories named after UIDs "exist" as soon as they are referenced and as long as they are non-empty. You can use autofs + tmpfs, or a variant of tmpfs for this. Solving (b) in a way that does not add a new ccache type (though having a KEYRING: ccache type that means "find the ccache URI in my keyring" is fine) is important because many of us run multiple implementations of Kerberos on any given host, and we do it because: - vendors are always behind the curve - legacy software linked with old versions of libkrb5 and friends - third party software Please review the above. Thanks, Nico -- -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Aug 2, 2013 at 3:49 PM, Nico Williams <nico@cryptonector.com> wrote: > Solving (b) in a way that does not add a new ccache type (though > having a KEYRING: ccache type that means "find the ccache URI in my > keyring" is fine) is important because many of us run multiple > implementations of Kerberos on any given host, and we do it because: > > - vendors are always behind the curve > - legacy software linked with old versions of libkrb5 and friends > - third party software I forgot to add: - special needs Nico -- -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Nico Williams <nico@cryptonector.com> wrote: > b) how to create tmpfs locations in which to store credentials (which > can be unbounded in size, so storing them in the kernel is silly; Ummm... tmpfs stores them in the kernel too - though it can page them out to swap. I have altered my big-key implementation to just store small items in an internal buffer and big items in a tmpfs file. This means that small items will use up _less_ kernel memory if they're in a key because they won't require the overhead of a dentry struct and an inode struct. > (b) can be solved in many ways, and the simplest is to have a > filesystem where top-level directories named after UIDs "exist" as > soon as they are referenced and as long as they are non-empty. You > can use autofs + tmpfs, or a variant of tmpfs for this. Don't forget to add user namespaces into the mix :-/ David -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Aug 8, 2013 at 9:46 AM, David Howells <dhowells@redhat.com> wrote: > Nico Williams <nico@cryptonector.com> wrote: > >> b) how to create tmpfs locations in which to store credentials (which >> can be unbounded in size, so storing them in the kernel is silly; > > Ummm... tmpfs stores them in the kernel too - though it can page them out to > swap. Any filesystem uses kernel, doesn't it? Notionally, however, they don't. And notionally tmpfs is not about using memory but about being non-persistent. This is important because tmpfs *is* subject to swapping and that does create security problems (you should want to encrypt swap). There's a difference between "notionally not persistent across reboots" and "notionally stored in the kernel's address space". The real question is: why do you want the latter? Some people have argued (not in this thread, and not recently on this list) that credentials should be stored in the kernel and not leave it (i.e., you shouldn't be able to extract them from the kernel). This implies a number of things, but one of those is a bad thing: unbounded kernel credential caches OR LRU/LFU cache eviction. Some of us rely on caching to keep latency predictable, so cache eviction could be a bit rough. Unbounded kernel credential caches not subject to paging are a DoS. Unbounded-but-paged cred caches would be fine, but really, what's the difference vis-a-vis FILE ccaches in /tmp? The answer depends on whether you allow the creds to leave kernel space. That brings us to: what do we really want? If we really want users to not be able to see their tickets' session keys, then we don't need to store their tickets in kernel land and we don't need to put krb5_mk_req*() in the kernel either: we need only *wrap* the keys to that low-level crypto use of those keys happens in the kernel and the only thing the kernel stores is the wrapping key. You might even have a TPM or similar crypto decelerator handle this. ISTM that a new ccache type whose purpose is to defeat the tmpfs race conditions of the FILE ccache is a good thing. I would prefer it to be *portable*, and it could be, but keyring stuff isn't. And FILE ccache remains the lowest common denominator, so there's that too. And since I've been dealing with some FILE ccache race conditions (see new thread I'll start next), I think a) I hate FILE ccaches, b) I don't see how to avoid having them. > I have altered my big-key implementation to just store small items in an > internal buffer and big items in a tmpfs file. This means that small items > will use up _less_ kernel memory if they're in a key because they won't require > the overhead of a dentry struct and an inode struct. Please make sure to have a cache eviction policy... I don't know what is "small" when it comes to Kerberos credentials either. If the difference between small and large is "lacks / has PAC and/or CAMMAC" then you'll find that this difference mostly depends on the environment, and generally your items will be either all "small" or all large. Are you building dead code? >> (b) can be solved in many ways, and the simplest is to have a >> filesystem where top-level directories named after UIDs "exist" as >> soon as they are referenced and as long as they are non-empty. You >> can use autofs + tmpfs, or a variant of tmpfs for this. > > Don't forget to add user namespaces into the mix :-/ Sure; why the ambivalence smiley? Nico -- -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/include/keys/big_key-type.h b/include/keys/big_key-type.h new file mode 100644 index 0000000..6610d46 --- /dev/null +++ b/include/keys/big_key-type.h @@ -0,0 +1,27 @@ +/* Big 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 License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _KEYS_BIG_KEY_TYPE_H +#define _KEYS_BIG_KEY_TYPE_H + +#include <linux/key-type.h> + +extern struct key_type key_type_big_key; + +struct key_preparsed_payload; + +extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern void big_key_revoke(struct key *key); +extern void big_key_destroy(struct key *key); +extern void big_key_describe(const struct key *big_key, struct seq_file *m); +extern long big_key_read(const struct key *key, char __user *buffer, size_t buflen); + +#endif /* _KEYS_BIG_KEY_TYPE_H */ diff --git a/include/linux/key.h b/include/linux/key.h index 2417f78..010dbb6 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -201,6 +201,7 @@ struct key { unsigned long value; void __rcu *rcudata; void *data; + void *data2[2]; } payload; struct assoc_array keys; }; diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 15e0dfe..eafb335 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -20,6 +20,17 @@ config KEYS If you are unsure as to whether this is required, answer N. +config BIG_KEYS + tristate "Large payload keys" + depends on KEYS + select 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 504aaa0..c487c77 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_SYSCTL) += sysctl.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 new file mode 100644 index 0000000..bd19afc --- /dev/null +++ b/security/keys/big_key.c @@ -0,0 +1,181 @@ +/* Big 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"); + +/* + * 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; + + /* 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)) + goto err_quota; + + written = kernel_write(file, prep->data, prep->datalen, 0); + if (written != datalen) { + 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); + return 0; + +err_fput: + fput(file); +err_quota: + key_payload_reserve(key, 0); +error: + return ret; +} + +/* + * match big_keys on their name + */ +int big_key_match(const struct key *key, const void *description) +{ + return strcmp(key->description, description) == 0; +} + +/* + * 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)) + vfs_truncate(path, 0); +} + +/* + * dispose of the data dangling from the corpse of a big_key key + */ +void big_key_destroy(struct key *key) +{ + struct path *path = (struct path *)&key->payload.data2; + path_put(path); + path->mnt = NULL; + path->dentry = NULL; +} + +/* + * describe the big_key key + */ +void big_key_describe(const struct key *key, struct seq_file *m) +{ + struct path *path = (struct path *)&key->payload.data2; + struct dentry *dentry = path->dentry; + struct inode *inode = dentry ? dentry->d_inode : NULL; + + seq_puts(m, key->description); + + if (inode && + key_is_instantiated(key) && + !test_bit(KEY_FLAG_REVOKED, &key->flags)) + seq_printf(m, ": %llu", i_size_read(inode)); +} + +/* + * 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) +{ + struct path *path = (struct path *)&key->payload.data2; + struct file *file; + loff_t pos, size = i_size_read(path->dentry->d_inode); + long ret; + + ret = size; + if (buffer && buflen >= size) { + file = dentry_open(path, O_RDONLY, current_cred()); + if (IS_ERR(file)) + return PTR_ERR(file); + + pos = 0; + ret = vfs_read(file, buffer, size, &pos); + fput(file); + if (ret >= 0 && ret != size) + return -EIO; + } + + 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);
Implement a big key type that can save its contents to tmpfs and thus swapspace when memory is tight. This is useful for Kerberos ticket caches. Signed-off-by: David Howells <dhowells@redhat.com> --- include/keys/big_key-type.h | 27 ++++++ include/linux/key.h | 1 security/keys/Kconfig | 11 +++ security/keys/Makefile | 1 security/keys/big_key.c | 181 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 include/keys/big_key-type.h create mode 100644 security/keys/big_key.c -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html