Skip to content

Commit

Permalink
KEYS: Implement a big key type that can save to tmpfs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Howells committed Sep 24, 2013
1 parent b2a4df2 commit ab3c358
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 0 deletions.
25 changes: 25 additions & 0 deletions include/keys/big_key-type.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* 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;

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 */
1 change: 1 addition & 0 deletions include/linux/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ struct key {
unsigned long value;
void __rcu *rcudata;
void *data;
void *data2[2];
} payload;
struct assoc_array keys;
};
Expand Down
11 changes: 11 additions & 0 deletions security/keys/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
Expand Down
1 change: 1 addition & 0 deletions security/keys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
204 changes: 204 additions & 0 deletions security/keys/big_key.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/* 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))
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);
} 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);

0 comments on commit ab3c358

Please sign in to comment.