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 | |||
