diff options
author | David Howells <dhowells@redhat.com> | 2013-09-24 05:35:18 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2013-09-24 05:35:18 -0400 |
commit | ab3c3587f8cda9083209a61dbe3a4407d3cada10 (patch) | |
tree | c479efb4b7a834ee5d3b74d81560a2214c463431 /security/keys | |
parent | b2a4df200d570b2c33a57e1ebfa5896e4bc81b69 (diff) |
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>
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/Kconfig | 11 | ||||
-rw-r--r-- | security/keys/Makefile | 1 | ||||
-rw-r--r-- | security/keys/big_key.c | 204 |
3 files changed, 216 insertions, 0 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 15e0dfe8c80f..b56362275ec8 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig | |||
@@ -20,6 +20,17 @@ config KEYS | |||
20 | 20 | ||
21 | If you are unsure as to whether this is required, answer N. | 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 | config TRUSTED_KEYS | 34 | config TRUSTED_KEYS |
24 | tristate "TRUSTED KEYS" | 35 | tristate "TRUSTED KEYS" |
25 | depends on KEYS && TCG_TPM | 36 | depends on KEYS && TCG_TPM |
diff --git a/security/keys/Makefile b/security/keys/Makefile index 504aaa008388..c487c77a00be 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile | |||
@@ -22,5 +22,6 @@ obj-$(CONFIG_SYSCTL) += sysctl.o | |||
22 | # | 22 | # |
23 | # Key types | 23 | # Key types |
24 | # | 24 | # |
25 | obj-$(CONFIG_BIG_KEYS) += big_key.o | ||
25 | obj-$(CONFIG_TRUSTED_KEYS) += trusted.o | 26 | obj-$(CONFIG_TRUSTED_KEYS) += trusted.o |
26 | obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ | 27 | 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 000000000000..5f9defc4a807 --- /dev/null +++ b/security/keys/big_key.c | |||
@@ -0,0 +1,204 @@ | |||
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); | ||