Commit ab3c3587f8cda9083209a61dbe3a4407d3cada10

Authored by David Howells
1 parent b2a4df200d

KEYS: Implement a big key type that can save to tmpfs

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>
Tested-by: Simo Sorce <simo@redhat.com>

Showing 5 changed files with 242 additions and 0 deletions Side-by-side Diff

include/keys/big_key-type.h
  1 +/* Big capacity key type.
  2 + *
  3 + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
  4 + * Written by David Howells (dhowells@redhat.com)
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License
  8 + * as published by the Free Software Foundation; either version
  9 + * 2 of the License, or (at your option) any later version.
  10 + */
  11 +
  12 +#ifndef _KEYS_BIG_KEY_TYPE_H
  13 +#define _KEYS_BIG_KEY_TYPE_H
  14 +
  15 +#include <linux/key-type.h>
  16 +
  17 +extern struct key_type key_type_big_key;
  18 +
  19 +extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
  20 +extern void big_key_revoke(struct key *key);
  21 +extern void big_key_destroy(struct key *key);
  22 +extern void big_key_describe(const struct key *big_key, struct seq_file *m);
  23 +extern long big_key_read(const struct key *key, char __user *buffer, size_t buflen);
  24 +
  25 +#endif /* _KEYS_BIG_KEY_TYPE_H */
... ... @@ -201,6 +201,7 @@
201 201 unsigned long value;
202 202 void __rcu *rcudata;
203 203 void *data;
  204 + void *data2[2];
204 205 } payload;
205 206 struct assoc_array keys;
206 207 };
security/keys/Kconfig
... ... @@ -20,6 +20,17 @@
20 20  
21 21 If you are unsure as to whether this is required, answer N.
22 22  
  23 +config BIG_KEYS
  24 + tristate "Large payload keys"
  25 + depends on KEYS
  26 + depends on TMPFS
  27 + help
  28 + This option provides support for holding large keys within the kernel
  29 + (for example Kerberos ticket caches). The data may be stored out to
  30 + swapspace by tmpfs.
  31 +
  32 + If you are unsure as to whether this is required, answer N.
  33 +
23 34 config TRUSTED_KEYS
24 35 tristate "TRUSTED KEYS"
25 36 depends on KEYS && TCG_TPM
security/keys/Makefile
... ... @@ -22,6 +22,7 @@
22 22 #
23 23 # Key types
24 24 #
  25 +obj-$(CONFIG_BIG_KEYS) += big_key.o
25 26 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
26 27 obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
security/keys/big_key.c
  1 +/* Large capacity key type
  2 + *
  3 + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
  4 + * Written by David Howells (dhowells@redhat.com)
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public Licence
  8 + * as published by the Free Software Foundation; either version
  9 + * 2 of the Licence, or (at your option) any later version.
  10 + */
  11 +
  12 +#include <linux/module.h>
  13 +#include <linux/init.h>
  14 +#include <linux/seq_file.h>
  15 +#include <linux/file.h>
  16 +#include <linux/shmem_fs.h>
  17 +#include <linux/err.h>
  18 +#include <keys/user-type.h>
  19 +#include <keys/big_key-type.h>
  20 +
  21 +MODULE_LICENSE("GPL");
  22 +
  23 +/*
  24 + * If the data is under this limit, there's no point creating a shm file to
  25 + * hold it as the permanently resident metadata for the shmem fs will be at
  26 + * least as large as the data.
  27 + */
  28 +#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
  29 +
  30 +/*
  31 + * big_key defined keys take an arbitrary string as the description and an
  32 + * arbitrary blob of data as the payload
  33 + */
  34 +struct key_type key_type_big_key = {
  35 + .name = "big_key",
  36 + .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
  37 + .instantiate = big_key_instantiate,
  38 + .match = user_match,
  39 + .revoke = big_key_revoke,
  40 + .destroy = big_key_destroy,
  41 + .describe = big_key_describe,
  42 + .read = big_key_read,
  43 +};
  44 +
  45 +/*
  46 + * Instantiate a big key
  47 + */
  48 +int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
  49 +{
  50 + struct path *path = (struct path *)&key->payload.data2;
  51 + struct file *file;
  52 + ssize_t written;
  53 + size_t datalen = prep->datalen;
  54 + int ret;
  55 +
  56 + ret = -EINVAL;
  57 + if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
  58 + goto error;
  59 +
  60 + /* Set an arbitrary quota */
  61 + ret = key_payload_reserve(key, 16);
  62 + if (ret < 0)
  63 + goto error;
  64 +
  65 + key->type_data.x[1] = datalen;
  66 +
  67 + if (datalen > BIG_KEY_FILE_THRESHOLD) {
  68 + /* Create a shmem file to store the data in. This will permit the data
  69 + * to be swapped out if needed.
  70 + *
  71 + * TODO: Encrypt the stored data with a temporary key.
  72 + */
  73 + file = shmem_file_setup("", datalen, 0);
  74 + if (IS_ERR(file))
  75 + goto err_quota;
  76 +
  77 + written = kernel_write(file, prep->data, prep->datalen, 0);
  78 + if (written != datalen) {
  79 + if (written >= 0)
  80 + ret = -ENOMEM;
  81 + goto err_fput;
  82 + }
  83 +
  84 + /* Pin the mount and dentry to the key so that we can open it again
  85 + * later
  86 + */
  87 + *path = file->f_path;
  88 + path_get(path);
  89 + fput(file);
  90 + } else {
  91 + /* Just store the data in a buffer */
  92 + void *data = kmalloc(datalen, GFP_KERNEL);
  93 + if (!data) {
  94 + ret = -ENOMEM;
  95 + goto err_quota;
  96 + }
  97 +
  98 + key->payload.data = memcpy(data, prep->data, prep->datalen);
  99 + }
  100 + return 0;
  101 +
  102 +err_fput:
  103 + fput(file);
  104 +err_quota:
  105 + key_payload_reserve(key, 0);
  106 +error:
  107 + return ret;
  108 +}
  109 +
  110 +/*
  111 + * dispose of the links from a revoked keyring
  112 + * - called with the key sem write-locked
  113 + */
  114 +void big_key_revoke(struct key *key)
  115 +{
  116 + struct path *path = (struct path *)&key->payload.data2;
  117 +
  118 + /* clear the quota */
  119 + key_payload_reserve(key, 0);
  120 + if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
  121 + vfs_truncate(path, 0);
  122 +}
  123 +
  124 +/*
  125 + * dispose of the data dangling from the corpse of a big_key key
  126 + */
  127 +void big_key_destroy(struct key *key)
  128 +{
  129 + if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
  130 + struct path *path = (struct path *)&key->payload.data2;
  131 + path_put(path);
  132 + path->mnt = NULL;
  133 + path->dentry = NULL;
  134 + } else {
  135 + kfree(key->payload.data);
  136 + key->payload.data = NULL;
  137 + }
  138 +}
  139 +
  140 +/*
  141 + * describe the big_key key
  142 + */
  143 +void big_key_describe(const struct key *key, struct seq_file *m)
  144 +{
  145 + unsigned long datalen = key->type_data.x[1];
  146 +
  147 + seq_puts(m, key->description);
  148 +
  149 + if (key_is_instantiated(key))
  150 + seq_printf(m, ": %lu [%s]",
  151 + datalen,
  152 + datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
  153 +}
  154 +
  155 +/*
  156 + * read the key data
  157 + * - the key's semaphore is read-locked
  158 + */
  159 +long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
  160 +{
  161 + unsigned long datalen = key->type_data.x[1];
  162 + long ret;
  163 +
  164 + if (!buffer || buflen < datalen)
  165 + return datalen;
  166 +
  167 + if (datalen > BIG_KEY_FILE_THRESHOLD) {
  168 + struct path *path = (struct path *)&key->payload.data2;
  169 + struct file *file;
  170 + loff_t pos;
  171 +
  172 + file = dentry_open(path, O_RDONLY, current_cred());
  173 + if (IS_ERR(file))
  174 + return PTR_ERR(file);
  175 +
  176 + pos = 0;
  177 + ret = vfs_read(file, buffer, datalen, &pos);
  178 + fput(file);
  179 + if (ret >= 0 && ret != datalen)
  180 + ret = -EIO;
  181 + } else {
  182 + ret = datalen;
  183 + if (copy_to_user(buffer, key->payload.data, datalen) != 0)
  184 + ret = -EFAULT;
  185 + }
  186 +
  187 + return ret;
  188 +}
  189 +
  190 +/*
  191 + * Module stuff
  192 + */
  193 +static int __init big_key_init(void)
  194 +{
  195 + return register_key_type(&key_type_big_key);
  196 +}
  197 +
  198 +static void __exit big_key_cleanup(void)
  199 +{
  200 + unregister_key_type(&key_type_big_key);
  201 +}
  202 +
  203 +module_init(big_key_init);
  204 +module_exit(big_key_cleanup);