Commit | Line | Data |
---|---|---|
b4d0d230 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ab3c3587 DH |
2 | /* Large capacity key type |
3 | * | |
521fd61c | 4 | * Copyright (C) 2017-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. |
ab3c3587 DH |
5 | * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. |
6 | * Written by David Howells (dhowells@redhat.com) | |
ab3c3587 DH |
7 | */ |
8 | ||
7df3e59c | 9 | #define pr_fmt(fmt) "big_key: "fmt |
ab3c3587 DH |
10 | #include <linux/init.h> |
11 | #include <linux/seq_file.h> | |
12 | #include <linux/file.h> | |
13 | #include <linux/shmem_fs.h> | |
14 | #include <linux/err.h> | |
428490e3 | 15 | #include <linux/random.h> |
ab3c3587 DH |
16 | #include <keys/user-type.h> |
17 | #include <keys/big_key-type.h> | |
521fd61c | 18 | #include <crypto/chacha20poly1305.h> |
d9f4bb1a | 19 | |
146aa8b1 DH |
20 | /* |
21 | * Layout of key payload words. | |
22 | */ | |
23 | enum { | |
24 | big_key_data, | |
25 | big_key_path, | |
26 | big_key_path_2nd_part, | |
27 | big_key_len, | |
28 | }; | |
29 | ||
ab3c3587 DH |
30 | /* |
31 | * If the data is under this limit, there's no point creating a shm file to | |
32 | * hold it as the permanently resident metadata for the shmem fs will be at | |
33 | * least as large as the data. | |
34 | */ | |
35 | #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) | |
36 | ||
37 | /* | |
38 | * big_key defined keys take an arbitrary string as the description and an | |
39 | * arbitrary blob of data as the payload | |
40 | */ | |
41 | struct key_type key_type_big_key = { | |
42 | .name = "big_key", | |
002edaf7 DH |
43 | .preparse = big_key_preparse, |
44 | .free_preparse = big_key_free_preparse, | |
45 | .instantiate = generic_key_instantiate, | |
ab3c3587 DH |
46 | .revoke = big_key_revoke, |
47 | .destroy = big_key_destroy, | |
48 | .describe = big_key_describe, | |
49 | .read = big_key_read, | |
b6f61c31 | 50 | .update = big_key_update, |
ab3c3587 DH |
51 | }; |
52 | ||
53 | /* | |
002edaf7 | 54 | * Preparse a big key |
ab3c3587 | 55 | */ |
002edaf7 | 56 | int big_key_preparse(struct key_preparsed_payload *prep) |
ab3c3587 | 57 | { |
146aa8b1 | 58 | struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
ab3c3587 | 59 | struct file *file; |
521fd61c | 60 | u8 *buf, *enckey; |
ab3c3587 | 61 | ssize_t written; |
521fd61c JD |
62 | size_t datalen = prep->datalen; |
63 | size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; | |
ab3c3587 DH |
64 | int ret; |
65 | ||
ab3c3587 | 66 | if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) |
d9f4bb1a | 67 | return -EINVAL; |
ab3c3587 DH |
68 | |
69 | /* Set an arbitrary quota */ | |
002edaf7 | 70 | prep->quotalen = 16; |
ab3c3587 | 71 | |
146aa8b1 | 72 | prep->payload.data[big_key_len] = (void *)(unsigned long)datalen; |
ab3c3587 DH |
73 | |
74 | if (datalen > BIG_KEY_FILE_THRESHOLD) { | |
75 | /* Create a shmem file to store the data in. This will permit the data | |
76 | * to be swapped out if needed. | |
77 | * | |
13100a72 | 78 | * File content is stored encrypted with randomly generated key. |
521fd61c JD |
79 | * Since the key is random for each file, we can set the nonce |
80 | * to zero, provided we never define a ->update() call. | |
ab3c3587 | 81 | */ |
e13ec939 | 82 | loff_t pos = 0; |
13100a72 | 83 | |
521fd61c | 84 | buf = kvmalloc(enclen, GFP_KERNEL); |
d9f4bb1a | 85 | if (!buf) |
13100a72 | 86 | return -ENOMEM; |
13100a72 KM |
87 | |
88 | /* generate random key */ | |
521fd61c | 89 | enckey = kmalloc(CHACHA20POLY1305_KEY_SIZE, GFP_KERNEL); |
13100a72 KM |
90 | if (!enckey) { |
91 | ret = -ENOMEM; | |
92 | goto error; | |
93 | } | |
521fd61c | 94 | ret = get_random_bytes_wait(enckey, CHACHA20POLY1305_KEY_SIZE); |
428490e3 | 95 | if (unlikely(ret)) |
13100a72 KM |
96 | goto err_enckey; |
97 | ||
521fd61c JD |
98 | /* encrypt data */ |
99 | chacha20poly1305_encrypt(buf, prep->data, datalen, NULL, 0, | |
100 | 0, enckey); | |
13100a72 KM |
101 | |
102 | /* save aligned data to file */ | |
103 | file = shmem_kernel_file_setup("", enclen, 0); | |
d2b86970 WY |
104 | if (IS_ERR(file)) { |
105 | ret = PTR_ERR(file); | |
13100a72 | 106 | goto err_enckey; |
d2b86970 | 107 | } |
ab3c3587 | 108 | |
521fd61c | 109 | written = kernel_write(file, buf, enclen, &pos); |
13100a72 | 110 | if (written != enclen) { |
97826c82 | 111 | ret = written; |
ab3c3587 | 112 | if (written >= 0) |
521fd61c | 113 | ret = -EIO; |
ab3c3587 DH |
114 | goto err_fput; |
115 | } | |
116 | ||
117 | /* Pin the mount and dentry to the key so that we can open it again | |
118 | * later | |
119 | */ | |
13100a72 | 120 | prep->payload.data[big_key_data] = enckey; |
ab3c3587 DH |
121 | *path = file->f_path; |
122 | path_get(path); | |
123 | fput(file); | |
272a1219 | 124 | kvfree_sensitive(buf, enclen); |
ab3c3587 DH |
125 | } else { |
126 | /* Just store the data in a buffer */ | |
127 | void *data = kmalloc(datalen, GFP_KERNEL); | |
13100a72 | 128 | |
002edaf7 DH |
129 | if (!data) |
130 | return -ENOMEM; | |
ab3c3587 | 131 | |
146aa8b1 DH |
132 | prep->payload.data[big_key_data] = data; |
133 | memcpy(data, prep->data, prep->datalen); | |
ab3c3587 DH |
134 | } |
135 | return 0; | |
136 | ||
137 | err_fput: | |
138 | fput(file); | |
13100a72 | 139 | err_enckey: |
453431a5 | 140 | kfree_sensitive(enckey); |
ab3c3587 | 141 | error: |
272a1219 | 142 | kvfree_sensitive(buf, enclen); |
ab3c3587 DH |
143 | return ret; |
144 | } | |
145 | ||
002edaf7 DH |
146 | /* |
147 | * Clear preparsement. | |
148 | */ | |
149 | void big_key_free_preparse(struct key_preparsed_payload *prep) | |
150 | { | |
151 | if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { | |
146aa8b1 | 152 | struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
13100a72 | 153 | |
002edaf7 | 154 | path_put(path); |
002edaf7 | 155 | } |
453431a5 | 156 | kfree_sensitive(prep->payload.data[big_key_data]); |
002edaf7 DH |
157 | } |
158 | ||
ab3c3587 DH |
159 | /* |
160 | * dispose of the links from a revoked keyring | |
161 | * - called with the key sem write-locked | |
162 | */ | |
163 | void big_key_revoke(struct key *key) | |
164 | { | |
146aa8b1 | 165 | struct path *path = (struct path *)&key->payload.data[big_key_path]; |
ab3c3587 DH |
166 | |
167 | /* clear the quota */ | |
168 | key_payload_reserve(key, 0); | |
363b02da | 169 | if (key_is_positive(key) && |
146aa8b1 | 170 | (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) |
ab3c3587 DH |
171 | vfs_truncate(path, 0); |
172 | } | |
173 | ||
174 | /* | |
175 | * dispose of the data dangling from the corpse of a big_key key | |
176 | */ | |
177 | void big_key_destroy(struct key *key) | |
178 | { | |
146aa8b1 DH |
179 | size_t datalen = (size_t)key->payload.data[big_key_len]; |
180 | ||
13100a72 | 181 | if (datalen > BIG_KEY_FILE_THRESHOLD) { |
146aa8b1 | 182 | struct path *path = (struct path *)&key->payload.data[big_key_path]; |
13100a72 | 183 | |
ab3c3587 DH |
184 | path_put(path); |
185 | path->mnt = NULL; | |
186 | path->dentry = NULL; | |
ab3c3587 | 187 | } |
453431a5 | 188 | kfree_sensitive(key->payload.data[big_key_data]); |
13100a72 | 189 | key->payload.data[big_key_data] = NULL; |
ab3c3587 DH |
190 | } |
191 | ||
b6f61c31 DH |
192 | /* |
193 | * Update a big key | |
194 | */ | |
195 | int big_key_update(struct key *key, struct key_preparsed_payload *prep) | |
196 | { | |
197 | int ret; | |
198 | ||
199 | ret = key_payload_reserve(key, prep->datalen); | |
200 | if (ret < 0) | |
201 | return ret; | |
202 | ||
203 | if (key_is_positive(key)) | |
204 | big_key_destroy(key); | |
205 | ||
206 | return generic_key_instantiate(key, prep); | |
207 | } | |
208 | ||
ab3c3587 DH |
209 | /* |
210 | * describe the big_key key | |
211 | */ | |
212 | void big_key_describe(const struct key *key, struct seq_file *m) | |
213 | { | |
146aa8b1 | 214 | size_t datalen = (size_t)key->payload.data[big_key_len]; |
ab3c3587 DH |
215 | |
216 | seq_puts(m, key->description); | |
217 | ||
363b02da | 218 | if (key_is_positive(key)) |
146aa8b1 | 219 | seq_printf(m, ": %zu [%s]", |
ab3c3587 DH |
220 | datalen, |
221 | datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); | |
222 | } | |
223 | ||
224 | /* | |
225 | * read the key data | |
226 | * - the key's semaphore is read-locked | |
227 | */ | |
d3ec10aa | 228 | long big_key_read(const struct key *key, char *buffer, size_t buflen) |
ab3c3587 | 229 | { |
146aa8b1 | 230 | size_t datalen = (size_t)key->payload.data[big_key_len]; |
ab3c3587 DH |
231 | long ret; |
232 | ||
233 | if (!buffer || buflen < datalen) | |
234 | return datalen; | |
235 | ||
236 | if (datalen > BIG_KEY_FILE_THRESHOLD) { | |
146aa8b1 | 237 | struct path *path = (struct path *)&key->payload.data[big_key_path]; |
ab3c3587 | 238 | struct file *file; |
521fd61c JD |
239 | u8 *buf, *enckey = (u8 *)key->payload.data[big_key_data]; |
240 | size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; | |
bdd1d2d3 | 241 | loff_t pos = 0; |
13100a72 | 242 | |
521fd61c | 243 | buf = kvmalloc(enclen, GFP_KERNEL); |
d9f4bb1a | 244 | if (!buf) |
13100a72 | 245 | return -ENOMEM; |
ab3c3587 DH |
246 | |
247 | file = dentry_open(path, O_RDONLY, current_cred()); | |
13100a72 KM |
248 | if (IS_ERR(file)) { |
249 | ret = PTR_ERR(file); | |
250 | goto error; | |
251 | } | |
ab3c3587 | 252 | |
13100a72 | 253 | /* read file to kernel and decrypt */ |
521fd61c JD |
254 | ret = kernel_read(file, buf, enclen, &pos); |
255 | if (ret != enclen) { | |
256 | if (ret >= 0) | |
257 | ret = -EIO; | |
13100a72 KM |
258 | goto err_fput; |
259 | } | |
260 | ||
521fd61c JD |
261 | ret = chacha20poly1305_decrypt(buf, buf, enclen, NULL, 0, 0, |
262 | enckey) ? 0 : -EBADMSG; | |
263 | if (unlikely(ret)) | |
13100a72 KM |
264 | goto err_fput; |
265 | ||
266 | ret = datalen; | |
267 | ||
d3ec10aa | 268 | /* copy out decrypted data */ |
521fd61c | 269 | memcpy(buffer, buf, datalen); |
13100a72 KM |
270 | |
271 | err_fput: | |
272 | fput(file); | |
273 | error: | |
272a1219 | 274 | kvfree_sensitive(buf, enclen); |
ab3c3587 DH |
275 | } else { |
276 | ret = datalen; | |
d3ec10aa | 277 | memcpy(buffer, key->payload.data[big_key_data], datalen); |
ab3c3587 DH |
278 | } |
279 | ||
280 | return ret; | |
281 | } | |
282 | ||
13100a72 KM |
283 | /* |
284 | * Register key type | |
285 | */ | |
ab3c3587 DH |
286 | static int __init big_key_init(void) |
287 | { | |
521fd61c | 288 | return register_key_type(&key_type_big_key); |
13100a72 KM |
289 | } |
290 | ||
7df3e59c | 291 | late_initcall(big_key_init); |