diff options
author | Theodore Ts'o <tytso@mit.edu> | 2016-02-07 19:35:05 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2016-02-07 19:35:05 -0500 |
commit | 28b4c263961c47da84ed8b5be0b5116bad1133eb (patch) | |
tree | cf169b33d19264de0af048876e0276f06430f726 | |
parent | 36f90b0a2ddd60823fe193a85e60ff1906c2a9b3 (diff) |
ext4 crypto: revalidate dentry after adding or removing the key
Add a validation check for dentries for encrypted directory to make
sure we're not caching stale data after a key has been added or removed.
Also check to make sure that status of the encryption key is updated
when readdir(2) is executed.
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r-- | fs/ext4/crypto.c | 56 | ||||
-rw-r--r-- | fs/ext4/dir.c | 6 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
-rw-r--r-- | fs/ext4/namei.c | 18 |
4 files changed, 81 insertions, 0 deletions
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index c8021208a7eb..38f7562489bb 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c | |||
@@ -467,3 +467,59 @@ uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size) | |||
467 | return size; | 467 | return size; |
468 | return 0; | 468 | return 0; |
469 | } | 469 | } |
470 | |||
471 | /* | ||
472 | * Validate dentries for encrypted directories to make sure we aren't | ||
473 | * potentially caching stale data after a key has been added or | ||
474 | * removed. | ||
475 | */ | ||
476 | static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags) | ||
477 | { | ||
478 | struct inode *dir = d_inode(dentry->d_parent); | ||
479 | struct ext4_crypt_info *ci = EXT4_I(dir)->i_crypt_info; | ||
480 | int dir_has_key, cached_with_key; | ||
481 | |||
482 | if (!ext4_encrypted_inode(dir)) | ||
483 | return 0; | ||
484 | |||
485 | if (ci && ci->ci_keyring_key && | ||
486 | (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | | ||
487 | (1 << KEY_FLAG_REVOKED) | | ||
488 | (1 << KEY_FLAG_DEAD)))) | ||
489 | ci = NULL; | ||
490 | |||
491 | /* this should eventually be an flag in d_flags */ | ||
492 | cached_with_key = dentry->d_fsdata != NULL; | ||
493 | dir_has_key = (ci != NULL); | ||
494 | |||
495 | /* | ||
496 | * If the dentry was cached without the key, and it is a | ||
497 | * negative dentry, it might be a valid name. We can't check | ||
498 | * if the key has since been made available due to locking | ||
499 | * reasons, so we fail the validation so ext4_lookup() can do | ||
500 | * this check. | ||
501 | * | ||
502 | * We also fail the validation if the dentry was created with | ||
503 | * the key present, but we no longer have the key, or vice versa. | ||
504 | */ | ||
505 | if ((!cached_with_key && d_is_negative(dentry)) || | ||
506 | (!cached_with_key && dir_has_key) || | ||
507 | (cached_with_key && !dir_has_key)) { | ||
508 | #if 0 /* Revalidation debug */ | ||
509 | char buf[80]; | ||
510 | char *cp = simple_dname(dentry, buf, sizeof(buf)); | ||
511 | |||
512 | if (IS_ERR(cp)) | ||
513 | cp = (char *) "???"; | ||
514 | pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata, | ||
515 | cached_with_key, d_is_negative(dentry), | ||
516 | dir_has_key); | ||
517 | #endif | ||
518 | return 0; | ||
519 | } | ||
520 | return 1; | ||
521 | } | ||
522 | |||
523 | const struct dentry_operations ext4_encrypted_d_ops = { | ||
524 | .d_revalidate = ext4_d_revalidate, | ||
525 | }; | ||
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 1d1bca74f844..6d17f31a31d7 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) | |||
111 | int dir_has_error = 0; | 111 | int dir_has_error = 0; |
112 | struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; | 112 | struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; |
113 | 113 | ||
114 | if (ext4_encrypted_inode(inode)) { | ||
115 | err = ext4_get_encryption_info(inode); | ||
116 | if (err && err != -ENOKEY) | ||
117 | return err; | ||
118 | } | ||
119 | |||
114 | if (is_dx_dir(inode)) { | 120 | if (is_dx_dir(inode)) { |
115 | err = ext4_dx_readdir(file, ctx); | 121 | err = ext4_dx_readdir(file, ctx); |
116 | if (err != ERR_BAD_DX_DIR) { | 122 | if (err != ERR_BAD_DX_DIR) { |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0662b285dc8a..157b458a69d4 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2302,6 +2302,7 @@ struct page *ext4_encrypt(struct inode *inode, | |||
2302 | int ext4_decrypt(struct page *page); | 2302 | int ext4_decrypt(struct page *page); |
2303 | int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, | 2303 | int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, |
2304 | ext4_fsblk_t pblk, ext4_lblk_t len); | 2304 | ext4_fsblk_t pblk, ext4_lblk_t len); |
2305 | extern const struct dentry_operations ext4_encrypted_d_ops; | ||
2305 | 2306 | ||
2306 | #ifdef CONFIG_EXT4_FS_ENCRYPTION | 2307 | #ifdef CONFIG_EXT4_FS_ENCRYPTION |
2307 | int ext4_init_crypto(void); | 2308 | int ext4_init_crypto(void); |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 06574dd77614..5de8483f0062 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi | |||
1558 | struct ext4_dir_entry_2 *de; | 1558 | struct ext4_dir_entry_2 *de; |
1559 | struct buffer_head *bh; | 1559 | struct buffer_head *bh; |
1560 | 1560 | ||
1561 | if (ext4_encrypted_inode(dir)) { | ||
1562 | int res = ext4_get_encryption_info(dir); | ||
1563 | |||
1564 | /* | ||
1565 | * This should be a properly defined flag for | ||
1566 | * dentry->d_flags when we uplift this to the VFS. | ||
1567 | * d_fsdata is set to (void *) 1 if if the dentry is | ||
1568 | * created while the directory was encrypted and we | ||
1569 | * don't have access to the key. | ||
1570 | */ | ||
1571 | dentry->d_fsdata = NULL; | ||
1572 | if (ext4_encryption_info(dir)) | ||
1573 | dentry->d_fsdata = (void *) 1; | ||
1574 | d_set_d_op(dentry, &ext4_encrypted_d_ops); | ||
1575 | if (res && res != -ENOKEY) | ||
1576 | return ERR_PTR(res); | ||
1577 | } | ||
1578 | |||
1561 | if (dentry->d_name.len > EXT4_NAME_LEN) | 1579 | if (dentry->d_name.len > EXT4_NAME_LEN) |
1562 | return ERR_PTR(-ENAMETOOLONG); | 1580 | return ERR_PTR(-ENAMETOOLONG); |
1563 | 1581 | ||