Commit ab3c3587f8cda9083209a61dbe3a4407d3cada10
1 parent
b2a4df200d
Exists in
master
and in
16 other branches
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 */ |
include/linux/key.h
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
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); |