aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2016-02-07 19:35:05 -0500
committerTheodore Ts'o <tytso@mit.edu>2016-02-07 19:35:05 -0500
commit28b4c263961c47da84ed8b5be0b5116bad1133eb (patch)
treecf169b33d19264de0af048876e0276f06430f726
parent36f90b0a2ddd60823fe193a85e60ff1906c2a9b3 (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.c56
-rw-r--r--fs/ext4/dir.c6
-rw-r--r--fs/ext4/ext4.h1
-rw-r--r--fs/ext4/namei.c18
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 */
476static 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
523const 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,
2302int ext4_decrypt(struct page *page); 2302int ext4_decrypt(struct page *page);
2303int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, 2303int 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);
2305extern const struct dentry_operations ext4_encrypted_d_ops;
2305 2306
2306#ifdef CONFIG_EXT4_FS_ENCRYPTION 2307#ifdef CONFIG_EXT4_FS_ENCRYPTION
2307int ext4_init_crypto(void); 2308int 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