diff options
author | Eric Biggers <ebiggers@google.com> | 2017-04-24 13:00:10 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2017-05-04 11:44:37 -0400 |
commit | 17159420a6c18bb3515ff85598b5ccf1a572763d (patch) | |
tree | 562afc39b20e061cbdfaaf6e2db7854da95fbb45 | |
parent | 6b06cdee81d68a8a829ad8e8d0f31d6836744af9 (diff) |
fscrypt: introduce helper function for filename matching
Introduce a helper function fscrypt_match_name() which tests whether a
fscrypt_name matches a directory entry. Also clean up the magic numbers
and document things properly.
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r-- | fs/crypto/fname.c | 90 | ||||
-rw-r--r-- | fs/crypto/fscrypt_private.h | 2 | ||||
-rw-r--r-- | include/linux/fscrypt_notsupp.h | 9 | ||||
-rw-r--r-- | include/linux/fscrypt_supp.h | 78 |
4 files changed, 157 insertions, 22 deletions
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 15bf9c31a34d..d1bb02b1ee58 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c | |||
@@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode, | |||
159 | static const char *lookup_table = | 159 | static const char *lookup_table = |
160 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; | 160 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; |
161 | 161 | ||
162 | #define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) | ||
163 | |||
162 | /** | 164 | /** |
163 | * digest_encode() - | 165 | * digest_encode() - |
164 | * | 166 | * |
@@ -230,11 +232,14 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size); | |||
230 | int fscrypt_fname_alloc_buffer(const struct inode *inode, | 232 | int fscrypt_fname_alloc_buffer(const struct inode *inode, |
231 | u32 ilen, struct fscrypt_str *crypto_str) | 233 | u32 ilen, struct fscrypt_str *crypto_str) |
232 | { | 234 | { |
233 | unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen); | 235 | u32 olen = fscrypt_fname_encrypted_size(inode, ilen); |
236 | const u32 max_encoded_len = | ||
237 | max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE), | ||
238 | 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))); | ||
234 | 239 | ||
235 | crypto_str->len = olen; | 240 | crypto_str->len = olen; |
236 | if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2) | 241 | olen = max(olen, max_encoded_len); |
237 | olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2; | 242 | |
238 | /* | 243 | /* |
239 | * Allocated buffer can hold one more character to null-terminate the | 244 | * Allocated buffer can hold one more character to null-terminate the |
240 | * string | 245 | * string |
@@ -266,6 +271,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer); | |||
266 | * | 271 | * |
267 | * The caller must have allocated sufficient memory for the @oname string. | 272 | * The caller must have allocated sufficient memory for the @oname string. |
268 | * | 273 | * |
274 | * If the key is available, we'll decrypt the disk name; otherwise, we'll encode | ||
275 | * it for presentation. Short names are directly base64-encoded, while long | ||
276 | * names are encoded in fscrypt_digested_name format. | ||
277 | * | ||
269 | * Return: 0 on success, -errno on failure | 278 | * Return: 0 on success, -errno on failure |
270 | */ | 279 | */ |
271 | int fscrypt_fname_disk_to_usr(struct inode *inode, | 280 | int fscrypt_fname_disk_to_usr(struct inode *inode, |
@@ -274,7 +283,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, | |||
274 | struct fscrypt_str *oname) | 283 | struct fscrypt_str *oname) |
275 | { | 284 | { |
276 | const struct qstr qname = FSTR_TO_QSTR(iname); | 285 | const struct qstr qname = FSTR_TO_QSTR(iname); |
277 | char buf[24]; | 286 | struct fscrypt_digested_name digested_name; |
278 | 287 | ||
279 | if (fscrypt_is_dot_dotdot(&qname)) { | 288 | if (fscrypt_is_dot_dotdot(&qname)) { |
280 | oname->name[0] = '.'; | 289 | oname->name[0] = '.'; |
@@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, | |||
289 | if (inode->i_crypt_info) | 298 | if (inode->i_crypt_info) |
290 | return fname_decrypt(inode, iname, oname); | 299 | return fname_decrypt(inode, iname, oname); |
291 | 300 | ||
292 | if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) { | 301 | if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) { |
293 | oname->len = digest_encode(iname->name, iname->len, | 302 | oname->len = digest_encode(iname->name, iname->len, |
294 | oname->name); | 303 | oname->name); |
295 | return 0; | 304 | return 0; |
296 | } | 305 | } |
297 | if (hash) { | 306 | if (hash) { |
298 | memcpy(buf, &hash, 4); | 307 | digested_name.hash = hash; |
299 | memcpy(buf + 4, &minor_hash, 4); | 308 | digested_name.minor_hash = minor_hash; |
300 | } else { | 309 | } else { |
301 | memset(buf, 0, 8); | 310 | digested_name.hash = 0; |
311 | digested_name.minor_hash = 0; | ||
302 | } | 312 | } |
303 | memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16); | 313 | memcpy(digested_name.digest, |
314 | FSCRYPT_FNAME_DIGEST(iname->name, iname->len), | ||
315 | FSCRYPT_FNAME_DIGEST_SIZE); | ||
304 | oname->name[0] = '_'; | 316 | oname->name[0] = '_'; |
305 | oname->len = 1 + digest_encode(buf, 24, oname->name + 1); | 317 | oname->len = 1 + digest_encode((const char *)&digested_name, |
318 | sizeof(digested_name), oname->name + 1); | ||
306 | return 0; | 319 | return 0; |
307 | } | 320 | } |
308 | EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); | 321 | EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); |
@@ -336,10 +349,35 @@ int fscrypt_fname_usr_to_disk(struct inode *inode, | |||
336 | } | 349 | } |
337 | EXPORT_SYMBOL(fscrypt_fname_usr_to_disk); | 350 | EXPORT_SYMBOL(fscrypt_fname_usr_to_disk); |
338 | 351 | ||
352 | /** | ||
353 | * fscrypt_setup_filename() - prepare to search a possibly encrypted directory | ||
354 | * @dir: the directory that will be searched | ||
355 | * @iname: the user-provided filename being searched for | ||
356 | * @lookup: 1 if we're allowed to proceed without the key because it's | ||
357 | * ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot | ||
358 | * proceed without the key because we're going to create the dir_entry. | ||
359 | * @fname: the filename information to be filled in | ||
360 | * | ||
361 | * Given a user-provided filename @iname, this function sets @fname->disk_name | ||
362 | * to the name that would be stored in the on-disk directory entry, if possible. | ||
363 | * If the directory is unencrypted this is simply @iname. Else, if we have the | ||
364 | * directory's encryption key, then @iname is the plaintext, so we encrypt it to | ||
365 | * get the disk_name. | ||
366 | * | ||
367 | * Else, for keyless @lookup operations, @iname is the presented ciphertext, so | ||
368 | * we decode it to get either the ciphertext disk_name (for short names) or the | ||
369 | * fscrypt_digested_name (for long names). Non-@lookup operations will be | ||
370 | * impossible in this case, so we fail them with ENOKEY. | ||
371 | * | ||
372 | * If successful, fscrypt_free_filename() must be called later to clean up. | ||
373 | * | ||
374 | * Return: 0 on success, -errno on failure | ||
375 | */ | ||
339 | int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, | 376 | int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, |
340 | int lookup, struct fscrypt_name *fname) | 377 | int lookup, struct fscrypt_name *fname) |
341 | { | 378 | { |
342 | int ret = 0, bigname = 0; | 379 | int ret; |
380 | int digested; | ||
343 | 381 | ||
344 | memset(fname, 0, sizeof(struct fscrypt_name)); | 382 | memset(fname, 0, sizeof(struct fscrypt_name)); |
345 | fname->usr_fname = iname; | 383 | fname->usr_fname = iname; |
@@ -373,25 +411,37 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, | |||
373 | * We don't have the key and we are doing a lookup; decode the | 411 | * We don't have the key and we are doing a lookup; decode the |
374 | * user-supplied name | 412 | * user-supplied name |
375 | */ | 413 | */ |
376 | if (iname->name[0] == '_') | 414 | if (iname->name[0] == '_') { |
377 | bigname = 1; | 415 | if (iname->len != |
378 | if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43))) | 416 | 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))) |
379 | return -ENOENT; | 417 | return -ENOENT; |
418 | digested = 1; | ||
419 | } else { | ||
420 | if (iname->len > | ||
421 | BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)) | ||
422 | return -ENOENT; | ||
423 | digested = 0; | ||
424 | } | ||
380 | 425 | ||
381 | fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); | 426 | fname->crypto_buf.name = |
427 | kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE, | ||
428 | sizeof(struct fscrypt_digested_name)), | ||
429 | GFP_KERNEL); | ||
382 | if (fname->crypto_buf.name == NULL) | 430 | if (fname->crypto_buf.name == NULL) |
383 | return -ENOMEM; | 431 | return -ENOMEM; |
384 | 432 | ||
385 | ret = digest_decode(iname->name + bigname, iname->len - bigname, | 433 | ret = digest_decode(iname->name + digested, iname->len - digested, |
386 | fname->crypto_buf.name); | 434 | fname->crypto_buf.name); |
387 | if (ret < 0) { | 435 | if (ret < 0) { |
388 | ret = -ENOENT; | 436 | ret = -ENOENT; |
389 | goto errout; | 437 | goto errout; |
390 | } | 438 | } |
391 | fname->crypto_buf.len = ret; | 439 | fname->crypto_buf.len = ret; |
392 | if (bigname) { | 440 | if (digested) { |
393 | memcpy(&fname->hash, fname->crypto_buf.name, 4); | 441 | const struct fscrypt_digested_name *n = |
394 | memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4); | 442 | (const void *)fname->crypto_buf.name; |
443 | fname->hash = n->hash; | ||
444 | fname->minor_hash = n->minor_hash; | ||
395 | } else { | 445 | } else { |
396 | fname->disk_name.name = fname->crypto_buf.name; | 446 | fname->disk_name.name = fname->crypto_buf.name; |
397 | fname->disk_name.len = fname->crypto_buf.len; | 447 | fname->disk_name.len = fname->crypto_buf.len; |
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index e08ca6d1ca0f..1e1f8a361b75 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h | |||
@@ -13,8 +13,6 @@ | |||
13 | 13 | ||
14 | #include <linux/fscrypt_supp.h> | 14 | #include <linux/fscrypt_supp.h> |
15 | 15 | ||
16 | #define FS_FNAME_CRYPTO_DIGEST_SIZE 32 | ||
17 | |||
18 | /* Encryption parameters */ | 16 | /* Encryption parameters */ |
19 | #define FS_XTS_TWEAK_SIZE 16 | 17 | #define FS_XTS_TWEAK_SIZE 16 |
20 | #define FS_AES_128_ECB_KEY_SIZE 16 | 18 | #define FS_AES_128_ECB_KEY_SIZE 16 |
diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 3511ca798804..ec406aed2f2f 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h | |||
@@ -147,6 +147,15 @@ static inline int fscrypt_fname_usr_to_disk(struct inode *inode, | |||
147 | return -EOPNOTSUPP; | 147 | return -EOPNOTSUPP; |
148 | } | 148 | } |
149 | 149 | ||
150 | static inline bool fscrypt_match_name(const struct fscrypt_name *fname, | ||
151 | const u8 *de_name, u32 de_name_len) | ||
152 | { | ||
153 | /* Encryption support disabled; use standard comparison */ | ||
154 | if (de_name_len != fname->disk_name.len) | ||
155 | return false; | ||
156 | return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); | ||
157 | } | ||
158 | |||
150 | /* bio.c */ | 159 | /* bio.c */ |
151 | static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, | 160 | static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, |
152 | struct bio *bio) | 161 | struct bio *bio) |
diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index a140f47e9b27..e12c224a0d1e 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h | |||
@@ -57,6 +57,84 @@ extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, | |||
57 | extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, | 57 | extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, |
58 | struct fscrypt_str *); | 58 | struct fscrypt_str *); |
59 | 59 | ||
60 | #define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 | ||
61 | |||
62 | /* Extracts the second-to-last ciphertext block; see explanation below */ | ||
63 | #define FSCRYPT_FNAME_DIGEST(name, len) \ | ||
64 | ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \ | ||
65 | FS_CRYPTO_BLOCK_SIZE)) | ||
66 | |||
67 | #define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE | ||
68 | |||
69 | /** | ||
70 | * fscrypt_digested_name - alternate identifier for an on-disk filename | ||
71 | * | ||
72 | * When userspace lists an encrypted directory without access to the key, | ||
73 | * filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE | ||
74 | * bytes are shown in this abbreviated form (base64-encoded) rather than as the | ||
75 | * full ciphertext (base64-encoded). This is necessary to allow supporting | ||
76 | * filenames up to NAME_MAX bytes, since base64 encoding expands the length. | ||
77 | * | ||
78 | * To make it possible for filesystems to still find the correct directory entry | ||
79 | * despite not knowing the full on-disk name, we encode any filesystem-specific | ||
80 | * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups, | ||
81 | * followed by the second-to-last ciphertext block of the filename. Due to the | ||
82 | * use of the CBC-CTS encryption mode, the second-to-last ciphertext block | ||
83 | * depends on the full plaintext. (Note that ciphertext stealing causes the | ||
84 | * last two blocks to appear "flipped".) This makes collisions very unlikely: | ||
85 | * just a 1 in 2^128 chance for two filenames to collide even if they share the | ||
86 | * same filesystem-specific hashes. | ||
87 | * | ||
88 | * This scheme isn't strictly immune to intentional collisions because it's | ||
89 | * basically like a CBC-MAC, which isn't secure on variable-length inputs. | ||
90 | * However, generating a CBC-MAC collision requires the ability to choose | ||
91 | * arbitrary ciphertext, which won't normally be possible with filename | ||
92 | * encryption since it would require write access to the raw disk. | ||
93 | * | ||
94 | * Taking a real cryptographic hash like SHA-256 over the full ciphertext would | ||
95 | * be better in theory but would be less efficient and more complicated to | ||
96 | * implement, especially since the filesystem would need to calculate it for | ||
97 | * each directory entry examined during a search. | ||
98 | */ | ||
99 | struct fscrypt_digested_name { | ||
100 | u32 hash; | ||
101 | u32 minor_hash; | ||
102 | u8 digest[FSCRYPT_FNAME_DIGEST_SIZE]; | ||
103 | }; | ||
104 | |||
105 | /** | ||
106 | * fscrypt_match_name() - test whether the given name matches a directory entry | ||
107 | * @fname: the name being searched for | ||
108 | * @de_name: the name from the directory entry | ||
109 | * @de_name_len: the length of @de_name in bytes | ||
110 | * | ||
111 | * Normally @fname->disk_name will be set, and in that case we simply compare | ||
112 | * that to the name stored in the directory entry. The only exception is that | ||
113 | * if we don't have the key for an encrypted directory and a filename in it is | ||
114 | * very long, then we won't have the full disk_name and we'll instead need to | ||
115 | * match against the fscrypt_digested_name. | ||
116 | * | ||
117 | * Return: %true if the name matches, otherwise %false. | ||
118 | */ | ||
119 | static inline bool fscrypt_match_name(const struct fscrypt_name *fname, | ||
120 | const u8 *de_name, u32 de_name_len) | ||
121 | { | ||
122 | if (unlikely(!fname->disk_name.name)) { | ||
123 | const struct fscrypt_digested_name *n = | ||
124 | (const void *)fname->crypto_buf.name; | ||
125 | if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_')) | ||
126 | return false; | ||
127 | if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) | ||
128 | return false; | ||
129 | return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len), | ||
130 | n->digest, FSCRYPT_FNAME_DIGEST_SIZE); | ||
131 | } | ||
132 | |||
133 | if (de_name_len != fname->disk_name.len) | ||
134 | return false; | ||
135 | return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); | ||
136 | } | ||
137 | |||
60 | /* bio.c */ | 138 | /* bio.c */ |
61 | extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); | 139 | extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); |
62 | extern void fscrypt_pullback_bio_page(struct page **, bool); | 140 | extern void fscrypt_pullback_bio_page(struct page **, bool); |