]>
Commit | Line | Data |
---|---|---|
d5d0e8c7 MH |
1 | /* |
2 | * linux/fs/ext4/crypto_fname.c | |
3 | * | |
4 | * Copyright (C) 2015, Google, Inc. | |
5 | * | |
6 | * This contains functions for filename crypto management in ext4 | |
7 | * | |
8 | * Written by Uday Savagaonkar, 2014. | |
9 | * | |
10 | * This has not yet undergone a rigorous security audit. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <crypto/hash.h> | |
15 | #include <crypto/sha.h> | |
16 | #include <keys/encrypted-type.h> | |
17 | #include <keys/user-type.h> | |
18 | #include <linux/crypto.h> | |
19 | #include <linux/gfp.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/key.h> | |
d5d0e8c7 MH |
22 | #include <linux/list.h> |
23 | #include <linux/mempool.h> | |
24 | #include <linux/random.h> | |
25 | #include <linux/scatterlist.h> | |
26 | #include <linux/spinlock_types.h> | |
27 | ||
28 | #include "ext4.h" | |
29 | #include "ext4_crypto.h" | |
30 | #include "xattr.h" | |
31 | ||
32 | /** | |
33 | * ext4_dir_crypt_complete() - | |
34 | */ | |
35 | static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res) | |
36 | { | |
37 | struct ext4_completion_result *ecr = req->data; | |
38 | ||
39 | if (res == -EINPROGRESS) | |
40 | return; | |
41 | ecr->res = res; | |
42 | complete(&ecr->completion); | |
43 | } | |
44 | ||
45 | bool ext4_valid_filenames_enc_mode(uint32_t mode) | |
46 | { | |
47 | return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS); | |
48 | } | |
49 | ||
b7236e21 TT |
50 | static unsigned max_name_len(struct inode *inode) |
51 | { | |
52 | return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize : | |
53 | EXT4_NAME_LEN; | |
54 | } | |
55 | ||
d5d0e8c7 MH |
56 | /** |
57 | * ext4_fname_encrypt() - | |
58 | * | |
59 | * This function encrypts the input filename, and returns the length of the | |
60 | * ciphertext. Errors are returned as negative numbers. We trust the caller to | |
61 | * allocate sufficient memory to oname string. | |
62 | */ | |
b7236e21 | 63 | static int ext4_fname_encrypt(struct inode *inode, |
d5d0e8c7 MH |
64 | const struct qstr *iname, |
65 | struct ext4_str *oname) | |
66 | { | |
67 | u32 ciphertext_len; | |
68 | struct ablkcipher_request *req = NULL; | |
69 | DECLARE_EXT4_COMPLETION_RESULT(ecr); | |
b7236e21 TT |
70 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; |
71 | struct crypto_ablkcipher *tfm = ci->ci_ctfm; | |
d5d0e8c7 MH |
72 | int res = 0; |
73 | char iv[EXT4_CRYPTO_BLOCK_SIZE]; | |
d2299590 | 74 | struct scatterlist src_sg, dst_sg; |
b7236e21 | 75 | int padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK); |
d2299590 | 76 | char *workbuf, buf[32], *alloc_buf = NULL; |
b7236e21 | 77 | unsigned lim = max_name_len(inode); |
d5d0e8c7 | 78 | |
b7236e21 | 79 | if (iname->len <= 0 || iname->len > lim) |
d5d0e8c7 MH |
80 | return -EIO; |
81 | ||
82 | ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ? | |
83 | EXT4_CRYPTO_BLOCK_SIZE : iname->len; | |
a44cd7a0 | 84 | ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding); |
b7236e21 TT |
85 | ciphertext_len = (ciphertext_len > lim) |
86 | ? lim : ciphertext_len; | |
d5d0e8c7 | 87 | |
d2299590 TT |
88 | if (ciphertext_len <= sizeof(buf)) { |
89 | workbuf = buf; | |
90 | } else { | |
91 | alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); | |
92 | if (!alloc_buf) | |
93 | return -ENOMEM; | |
94 | workbuf = alloc_buf; | |
95 | } | |
96 | ||
d5d0e8c7 MH |
97 | /* Allocate request */ |
98 | req = ablkcipher_request_alloc(tfm, GFP_NOFS); | |
99 | if (!req) { | |
100 | printk_ratelimited( | |
101 | KERN_ERR "%s: crypto_request_alloc() failed\n", __func__); | |
d2299590 | 102 | kfree(alloc_buf); |
d5d0e8c7 MH |
103 | return -ENOMEM; |
104 | } | |
105 | ablkcipher_request_set_callback(req, | |
106 | CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, | |
107 | ext4_dir_crypt_complete, &ecr); | |
108 | ||
d5d0e8c7 MH |
109 | /* Copy the input */ |
110 | memcpy(workbuf, iname->name, iname->len); | |
111 | if (iname->len < ciphertext_len) | |
112 | memset(workbuf + iname->len, 0, ciphertext_len - iname->len); | |
113 | ||
114 | /* Initialize IV */ | |
115 | memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE); | |
116 | ||
117 | /* Create encryption request */ | |
d2299590 TT |
118 | sg_init_one(&src_sg, workbuf, ciphertext_len); |
119 | sg_init_one(&dst_sg, oname->name, ciphertext_len); | |
120 | ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); | |
d5d0e8c7 MH |
121 | res = crypto_ablkcipher_encrypt(req); |
122 | if (res == -EINPROGRESS || res == -EBUSY) { | |
d5d0e8c7 MH |
123 | wait_for_completion(&ecr.completion); |
124 | res = ecr.res; | |
125 | } | |
d2299590 | 126 | kfree(alloc_buf); |
d5d0e8c7 MH |
127 | ablkcipher_request_free(req); |
128 | if (res < 0) { | |
129 | printk_ratelimited( | |
130 | KERN_ERR "%s: Error (error code %d)\n", __func__, res); | |
131 | } | |
132 | oname->len = ciphertext_len; | |
133 | return res; | |
134 | } | |
135 | ||
136 | /* | |
137 | * ext4_fname_decrypt() | |
138 | * This function decrypts the input filename, and returns | |
139 | * the length of the plaintext. | |
140 | * Errors are returned as negative numbers. | |
141 | * We trust the caller to allocate sufficient memory to oname string. | |
142 | */ | |
b7236e21 | 143 | static int ext4_fname_decrypt(struct inode *inode, |
d5d0e8c7 MH |
144 | const struct ext4_str *iname, |
145 | struct ext4_str *oname) | |
146 | { | |
147 | struct ext4_str tmp_in[2], tmp_out[1]; | |
148 | struct ablkcipher_request *req = NULL; | |
149 | DECLARE_EXT4_COMPLETION_RESULT(ecr); | |
d2299590 | 150 | struct scatterlist src_sg, dst_sg; |
b7236e21 TT |
151 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; |
152 | struct crypto_ablkcipher *tfm = ci->ci_ctfm; | |
d5d0e8c7 MH |
153 | int res = 0; |
154 | char iv[EXT4_CRYPTO_BLOCK_SIZE]; | |
b7236e21 | 155 | unsigned lim = max_name_len(inode); |
d5d0e8c7 | 156 | |
b7236e21 | 157 | if (iname->len <= 0 || iname->len > lim) |
d5d0e8c7 MH |
158 | return -EIO; |
159 | ||
160 | tmp_in[0].name = iname->name; | |
161 | tmp_in[0].len = iname->len; | |
162 | tmp_out[0].name = oname->name; | |
163 | ||
164 | /* Allocate request */ | |
165 | req = ablkcipher_request_alloc(tfm, GFP_NOFS); | |
166 | if (!req) { | |
167 | printk_ratelimited( | |
168 | KERN_ERR "%s: crypto_request_alloc() failed\n", __func__); | |
169 | return -ENOMEM; | |
170 | } | |
171 | ablkcipher_request_set_callback(req, | |
172 | CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, | |
173 | ext4_dir_crypt_complete, &ecr); | |
174 | ||
d5d0e8c7 MH |
175 | /* Initialize IV */ |
176 | memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE); | |
177 | ||
178 | /* Create encryption request */ | |
d2299590 TT |
179 | sg_init_one(&src_sg, iname->name, iname->len); |
180 | sg_init_one(&dst_sg, oname->name, oname->len); | |
181 | ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); | |
d5d0e8c7 MH |
182 | res = crypto_ablkcipher_decrypt(req); |
183 | if (res == -EINPROGRESS || res == -EBUSY) { | |
d5d0e8c7 MH |
184 | wait_for_completion(&ecr.completion); |
185 | res = ecr.res; | |
186 | } | |
d5d0e8c7 MH |
187 | ablkcipher_request_free(req); |
188 | if (res < 0) { | |
189 | printk_ratelimited( | |
190 | KERN_ERR "%s: Error in ext4_fname_encrypt (error code %d)\n", | |
191 | __func__, res); | |
192 | return res; | |
193 | } | |
194 | ||
195 | oname->len = strnlen(oname->name, iname->len); | |
196 | return oname->len; | |
197 | } | |
198 | ||
5de0b4d0 TT |
199 | static const char *lookup_table = |
200 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; | |
201 | ||
d5d0e8c7 MH |
202 | /** |
203 | * ext4_fname_encode_digest() - | |
204 | * | |
205 | * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. | |
206 | * The encoded string is roughly 4/3 times the size of the input string. | |
207 | */ | |
5de0b4d0 | 208 | static int digest_encode(const char *src, int len, char *dst) |
d5d0e8c7 | 209 | { |
5de0b4d0 TT |
210 | int i = 0, bits = 0, ac = 0; |
211 | char *cp = dst; | |
212 | ||
213 | while (i < len) { | |
214 | ac += (((unsigned char) src[i]) << bits); | |
215 | bits += 8; | |
216 | do { | |
217 | *cp++ = lookup_table[ac & 0x3f]; | |
218 | ac >>= 6; | |
219 | bits -= 6; | |
220 | } while (bits >= 6); | |
d5d0e8c7 MH |
221 | i++; |
222 | } | |
5de0b4d0 TT |
223 | if (bits) |
224 | *cp++ = lookup_table[ac & 0x3f]; | |
225 | return cp - dst; | |
d5d0e8c7 MH |
226 | } |
227 | ||
5de0b4d0 | 228 | static int digest_decode(const char *src, int len, char *dst) |
d5d0e8c7 | 229 | { |
5de0b4d0 TT |
230 | int i = 0, bits = 0, ac = 0; |
231 | const char *p; | |
232 | char *cp = dst; | |
233 | ||
234 | while (i < len) { | |
235 | p = strchr(lookup_table, src[i]); | |
236 | if (p == NULL || src[i] == 0) | |
237 | return -2; | |
238 | ac += (p - lookup_table) << bits; | |
239 | bits += 6; | |
240 | if (bits >= 8) { | |
241 | *cp++ = ac & 0xff; | |
242 | ac >>= 8; | |
243 | bits -= 8; | |
244 | } | |
245 | i++; | |
d5d0e8c7 | 246 | } |
5de0b4d0 TT |
247 | if (ac) |
248 | return -1; | |
249 | return cp - dst; | |
d5d0e8c7 MH |
250 | } |
251 | ||
d5d0e8c7 MH |
252 | /** |
253 | * ext4_fname_crypto_round_up() - | |
254 | * | |
255 | * Return: The next multiple of block size | |
256 | */ | |
257 | u32 ext4_fname_crypto_round_up(u32 size, u32 blksize) | |
258 | { | |
259 | return ((size+blksize-1)/blksize)*blksize; | |
260 | } | |
261 | ||
4d3c4e5b TT |
262 | unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen) |
263 | { | |
264 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; | |
265 | int padding = 32; | |
266 | ||
267 | if (ci) | |
268 | padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK); | |
269 | if (ilen < EXT4_CRYPTO_BLOCK_SIZE) | |
270 | ilen = EXT4_CRYPTO_BLOCK_SIZE; | |
271 | return ext4_fname_crypto_round_up(ilen, padding); | |
272 | } | |
273 | ||
274 | /* | |
275 | * ext4_fname_crypto_alloc_buffer() - | |
d5d0e8c7 MH |
276 | * |
277 | * Allocates an output buffer that is sufficient for the crypto operation | |
278 | * specified by the context and the direction. | |
279 | */ | |
b7236e21 | 280 | int ext4_fname_crypto_alloc_buffer(struct inode *inode, |
d5d0e8c7 MH |
281 | u32 ilen, struct ext4_str *crypto_str) |
282 | { | |
4d3c4e5b | 283 | unsigned int olen = ext4_fname_encrypted_size(inode, ilen); |
d5d0e8c7 | 284 | |
d5d0e8c7 MH |
285 | crypto_str->len = olen; |
286 | if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) | |
287 | olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2; | |
288 | /* Allocated buffer can hold one more character to null-terminate the | |
289 | * string */ | |
290 | crypto_str->name = kmalloc(olen+1, GFP_NOFS); | |
291 | if (!(crypto_str->name)) | |
292 | return -ENOMEM; | |
293 | return 0; | |
294 | } | |
295 | ||
296 | /** | |
297 | * ext4_fname_crypto_free_buffer() - | |
298 | * | |
299 | * Frees the buffer allocated for crypto operation. | |
300 | */ | |
301 | void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str) | |
302 | { | |
303 | if (!crypto_str) | |
304 | return; | |
305 | kfree(crypto_str->name); | |
306 | crypto_str->name = NULL; | |
307 | } | |
308 | ||
309 | /** | |
310 | * ext4_fname_disk_to_usr() - converts a filename from disk space to user space | |
311 | */ | |
b7236e21 | 312 | int _ext4_fname_disk_to_usr(struct inode *inode, |
5de0b4d0 TT |
313 | struct dx_hash_info *hinfo, |
314 | const struct ext4_str *iname, | |
315 | struct ext4_str *oname) | |
d5d0e8c7 | 316 | { |
5de0b4d0 TT |
317 | char buf[24]; |
318 | int ret; | |
319 | ||
d5d0e8c7 MH |
320 | if (iname->len < 3) { |
321 | /*Check for . and .. */ | |
322 | if (iname->name[0] == '.' && iname->name[iname->len-1] == '.') { | |
323 | oname->name[0] = '.'; | |
324 | oname->name[iname->len-1] = '.'; | |
325 | oname->len = iname->len; | |
326 | return oname->len; | |
327 | } | |
328 | } | |
27977b69 TT |
329 | if (iname->len < EXT4_CRYPTO_BLOCK_SIZE) { |
330 | EXT4_ERROR_INODE(inode, "encrypted inode too small"); | |
331 | return -EUCLEAN; | |
332 | } | |
b7236e21 TT |
333 | if (EXT4_I(inode)->i_crypt_info) |
334 | return ext4_fname_decrypt(inode, iname, oname); | |
5de0b4d0 TT |
335 | |
336 | if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) { | |
337 | ret = digest_encode(iname->name, iname->len, oname->name); | |
338 | oname->len = ret; | |
339 | return ret; | |
340 | } | |
341 | if (hinfo) { | |
342 | memcpy(buf, &hinfo->hash, 4); | |
343 | memcpy(buf+4, &hinfo->minor_hash, 4); | |
344 | } else | |
345 | memset(buf, 0, 8); | |
346 | memcpy(buf + 8, iname->name + iname->len - 16, 16); | |
347 | oname->name[0] = '_'; | |
348 | ret = digest_encode(buf, 24, oname->name+1); | |
349 | oname->len = ret + 1; | |
350 | return ret + 1; | |
d5d0e8c7 MH |
351 | } |
352 | ||
b7236e21 | 353 | int ext4_fname_disk_to_usr(struct inode *inode, |
5de0b4d0 | 354 | struct dx_hash_info *hinfo, |
d5d0e8c7 MH |
355 | const struct ext4_dir_entry_2 *de, |
356 | struct ext4_str *oname) | |
357 | { | |
358 | struct ext4_str iname = {.name = (unsigned char *) de->name, | |
359 | .len = de->name_len }; | |
360 | ||
b7236e21 | 361 | return _ext4_fname_disk_to_usr(inode, hinfo, &iname, oname); |
d5d0e8c7 MH |
362 | } |
363 | ||
364 | ||
365 | /** | |
366 | * ext4_fname_usr_to_disk() - converts a filename from user space to disk space | |
367 | */ | |
b7236e21 | 368 | int ext4_fname_usr_to_disk(struct inode *inode, |
d5d0e8c7 MH |
369 | const struct qstr *iname, |
370 | struct ext4_str *oname) | |
371 | { | |
372 | int res; | |
b7236e21 | 373 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; |
d5d0e8c7 | 374 | |
d5d0e8c7 MH |
375 | if (iname->len < 3) { |
376 | /*Check for . and .. */ | |
377 | if (iname->name[0] == '.' && | |
378 | iname->name[iname->len-1] == '.') { | |
379 | oname->name[0] = '.'; | |
380 | oname->name[iname->len-1] = '.'; | |
381 | oname->len = iname->len; | |
382 | return oname->len; | |
383 | } | |
384 | } | |
b7236e21 TT |
385 | if (ci) { |
386 | res = ext4_fname_encrypt(inode, iname, oname); | |
d5d0e8c7 MH |
387 | return res; |
388 | } | |
389 | /* Without a proper key, a user is not allowed to modify the filenames | |
390 | * in a directory. Consequently, a user space name cannot be mapped to | |
391 | * a disk-space name */ | |
392 | return -EACCES; | |
393 | } | |
394 | ||
5b643f9c TT |
395 | int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname, |
396 | int lookup, struct ext4_filename *fname) | |
d5d0e8c7 | 397 | { |
b7236e21 | 398 | struct ext4_crypt_info *ci; |
5b643f9c TT |
399 | int ret = 0, bigname = 0; |
400 | ||
401 | memset(fname, 0, sizeof(struct ext4_filename)); | |
402 | fname->usr_fname = iname; | |
d5d0e8c7 | 403 | |
b7236e21 | 404 | if (!ext4_encrypted_inode(dir) || |
d5d0e8c7 MH |
405 | ((iname->name[0] == '.') && |
406 | ((iname->len == 1) || | |
407 | ((iname->name[1] == '.') && (iname->len == 2))))) { | |
5b643f9c TT |
408 | fname->disk_name.name = (unsigned char *) iname->name; |
409 | fname->disk_name.len = iname->len; | |
82d0d3e7 | 410 | return 0; |
d5d0e8c7 | 411 | } |
c936e1ec | 412 | ret = ext4_get_encryption_info(dir); |
b7236e21 TT |
413 | if (ret) |
414 | return ret; | |
415 | ci = EXT4_I(dir)->i_crypt_info; | |
416 | if (ci) { | |
417 | ret = ext4_fname_crypto_alloc_buffer(dir, iname->len, | |
5b643f9c TT |
418 | &fname->crypto_buf); |
419 | if (ret < 0) | |
82d0d3e7 | 420 | return ret; |
b7236e21 | 421 | ret = ext4_fname_encrypt(dir, iname, &fname->crypto_buf); |
5b643f9c | 422 | if (ret < 0) |
82d0d3e7 | 423 | goto errout; |
5b643f9c TT |
424 | fname->disk_name.name = fname->crypto_buf.name; |
425 | fname->disk_name.len = fname->crypto_buf.len; | |
82d0d3e7 | 426 | return 0; |
5de0b4d0 | 427 | } |
82d0d3e7 TT |
428 | if (!lookup) |
429 | return -EACCES; | |
5de0b4d0 | 430 | |
5b643f9c TT |
431 | /* We don't have the key and we are doing a lookup; decode the |
432 | * user-supplied name | |
433 | */ | |
434 | if (iname->name[0] == '_') | |
435 | bigname = 1; | |
436 | if ((bigname && (iname->len != 33)) || | |
82d0d3e7 TT |
437 | (!bigname && (iname->len > 43))) |
438 | return -ENOENT; | |
439 | ||
5b643f9c | 440 | fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); |
82d0d3e7 TT |
441 | if (fname->crypto_buf.name == NULL) |
442 | return -ENOMEM; | |
5b643f9c TT |
443 | ret = digest_decode(iname->name + bigname, iname->len - bigname, |
444 | fname->crypto_buf.name); | |
445 | if (ret < 0) { | |
446 | ret = -ENOENT; | |
82d0d3e7 | 447 | goto errout; |
5b643f9c TT |
448 | } |
449 | fname->crypto_buf.len = ret; | |
450 | if (bigname) { | |
451 | memcpy(&fname->hinfo.hash, fname->crypto_buf.name, 4); | |
452 | memcpy(&fname->hinfo.minor_hash, fname->crypto_buf.name + 4, 4); | |
453 | } else { | |
454 | fname->disk_name.name = fname->crypto_buf.name; | |
455 | fname->disk_name.len = fname->crypto_buf.len; | |
456 | } | |
82d0d3e7 TT |
457 | return 0; |
458 | errout: | |
459 | kfree(fname->crypto_buf.name); | |
460 | fname->crypto_buf.name = NULL; | |
d5d0e8c7 MH |
461 | return ret; |
462 | } | |
463 | ||
5b643f9c | 464 | void ext4_fname_free_filename(struct ext4_filename *fname) |
d5d0e8c7 | 465 | { |
5b643f9c TT |
466 | kfree(fname->crypto_buf.name); |
467 | fname->crypto_buf.name = NULL; | |
468 | fname->usr_fname = NULL; | |
469 | fname->disk_name.name = NULL; | |
d5d0e8c7 | 470 | } |