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>
This commit is contained in:
parent
b2a4df200d
commit
ab3c3587f8
5 changed files with 242 additions and 0 deletions
25
include/keys/big_key-type.h
Normal file
25
include/keys/big_key-type.h
Normal file
|
@ -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 */
|
|
@ -201,6 +201,7 @@ struct key {
|
|||
unsigned long value;
|
||||
void __rcu *rcudata;
|
||||
void *data;
|
||||
void *data2[2];
|
||||
} payload;
|
||||
struct assoc_array keys;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
security/keys/big_key.c
Normal file
204
security/keys/big_key.c
Normal file
|
@ -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);
|
Loading…
Reference in a new issue