diff options
Diffstat (limited to 'fs/crypto')
-rw-r--r-- | fs/crypto/fname.c | 30 | ||||
-rw-r--r-- | fs/crypto/fscrypt_private.h | 14 | ||||
-rw-r--r-- | fs/crypto/hooks.c | 47 | ||||
-rw-r--r-- | fs/crypto/inline_crypt.c | 6 | ||||
-rw-r--r-- | fs/crypto/keyring.c | 11 | ||||
-rw-r--r-- | fs/crypto/keysetup.c | 11 |
6 files changed, 64 insertions, 55 deletions
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 7b3fc189593a..010f9c0a4c2f 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -74,13 +74,7 @@ struct fscrypt_nokey_name { static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) { - if (str->len == 1 && str->name[0] == '.') - return true; - - if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') - return true; - - return false; + return is_dot_dotdot(str->name, str->len); } /** @@ -580,11 +574,10 @@ EXPORT_SYMBOL_GPL(fscrypt_fname_siphash); * Validate dentries in encrypted directories to make sure we aren't potentially * caching stale dentries after a key has been added. */ -int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) +int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name, + struct dentry *dentry, unsigned int flags) { - struct dentry *dir; int err; - int valid; /* * Plaintext names are always valid, since fscrypt doesn't support @@ -597,30 +590,21 @@ int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) /* * No-key name; valid if the directory's key is still unavailable. * - * Although fscrypt forbids rename() on no-key names, we still must use - * dget_parent() here rather than use ->d_parent directly. That's - * because a corrupted fs image may contain directory hard links, which - * the VFS handles by moving the directory's dentry tree in the dcache - * each time ->lookup() finds the directory and it already has a dentry - * elsewhere. Thus ->d_parent can be changing, and we must safely grab - * a reference to some ->d_parent to prevent it from being freed. + * Note in RCU mode we have to bail if we get here - + * fscrypt_get_encryption_info() may block. */ if (flags & LOOKUP_RCU) return -ECHILD; - dir = dget_parent(dentry); /* * Pass allow_unsupported=true, so that files with an unsupported * encryption policy can be deleted. */ - err = fscrypt_get_encryption_info(d_inode(dir), true); - valid = !fscrypt_has_encryption_key(d_inode(dir)); - dput(dir); - + err = fscrypt_get_encryption_info(dir, true); if (err < 0) return err; - return valid; + return !fscrypt_has_encryption_key(dir); } EXPORT_SYMBOL_GPL(fscrypt_d_revalidate); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 1892356cf924..8371e4e1f596 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -222,16 +222,19 @@ struct fscrypt_inode_info { struct fscrypt_prepared_key ci_enc_key; /* True if ci_enc_key should be freed when this struct is freed */ - bool ci_owns_key; + u8 ci_owns_key : 1; #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT /* * True if this inode will use inline encryption (blk-crypto) instead of * the traditional filesystem-layer encryption. */ - bool ci_inlinecrypt; + u8 ci_inlinecrypt : 1; #endif + /* True if ci_dirhash_key is initialized */ + u8 ci_dirhash_key_initialized : 1; + /* * log2 of the data unit size (granularity of contents encryption) of * this file. This is computable from ci_policy and ci_inode but is @@ -242,6 +245,9 @@ struct fscrypt_inode_info { /* Cached value: log2 of number of data units per FS block */ u8 ci_data_units_per_block_bits; + /* Hashed inode number. Only set for IV_INO_LBLK_32 */ + u32 ci_hashed_ino; + /* * Encryption mode used for this inode. It corresponds to either the * contents or filenames encryption mode, depending on the inode type. @@ -276,16 +282,12 @@ struct fscrypt_inode_info { * the plaintext filenames -- currently just casefolded directories. */ siphash_key_t ci_dirhash_key; - bool ci_dirhash_key_initialized; /* The encryption policy used by this inode */ union fscrypt_policy ci_policy; /* This inode's nonce, copied from the fscrypt_context */ u8 ci_nonce[FSCRYPT_FILE_NONCE_SIZE]; - - /* Hashed inode number. Only set for IV_INO_LBLK_32 */ - u32 ci_hashed_ino; }; typedef enum { diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 52504dd478d3..d8d5049b8fe1 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -30,21 +30,41 @@ int fscrypt_file_open(struct inode *inode, struct file *filp) { int err; - struct dentry *dir; + struct dentry *dentry, *dentry_parent; + struct inode *inode_parent; err = fscrypt_require_key(inode); if (err) return err; - dir = dget_parent(file_dentry(filp)); - if (IS_ENCRYPTED(d_inode(dir)) && - !fscrypt_has_permitted_context(d_inode(dir), inode)) { + dentry = file_dentry(filp); + + /* + * Getting a reference to the parent dentry is needed for the actual + * encryption policy comparison, but it's expensive on multi-core + * systems. Since this function runs on unencrypted files too, start + * with a lightweight RCU-mode check for the parent directory being + * unencrypted (in which case it's fine for the child to be either + * unencrypted, or encrypted with any policy). Only continue on to the + * full policy check if the parent directory is actually encrypted. + */ + rcu_read_lock(); + dentry_parent = READ_ONCE(dentry->d_parent); + inode_parent = d_inode_rcu(dentry_parent); + if (inode_parent != NULL && !IS_ENCRYPTED(inode_parent)) { + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + + dentry_parent = dget_parent(dentry); + if (!fscrypt_has_permitted_context(d_inode(dentry_parent), inode)) { fscrypt_warn(inode, "Inconsistent encryption context (parent directory: %lu)", - d_inode(dir)->i_ino); + d_inode(dentry_parent)->i_ino); err = -EPERM; } - dput(dir); + dput(dentry_parent); return err; } EXPORT_SYMBOL_GPL(fscrypt_file_open); @@ -102,11 +122,8 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, if (err && err != -ENOENT) return err; - if (fname->is_nokey_name) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_NOKEY_NAME; - spin_unlock(&dentry->d_lock); - } + fscrypt_prepare_dentry(dentry, fname->is_nokey_name); + return err; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); @@ -131,12 +148,10 @@ EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); int fscrypt_prepare_lookup_partial(struct inode *dir, struct dentry *dentry) { int err = fscrypt_get_encryption_info(dir, true); + bool is_nokey_name = (!err && !fscrypt_has_encryption_key(dir)); + + fscrypt_prepare_dentry(dentry, is_nokey_name); - if (!err && !fscrypt_has_encryption_key(dir)) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_NOKEY_NAME; - spin_unlock(&dentry->d_lock); - } return err; } EXPORT_SYMBOL_GPL(fscrypt_prepare_lookup_partial); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index b4002aea7cdb..40de69860dcf 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -284,7 +284,7 @@ static bool bh_get_inode_and_lblk_num(const struct buffer_head *bh, const struct inode **inode_ret, u64 *lblk_num_ret) { - struct page *page = bh->b_page; + struct folio *folio = bh->b_folio; const struct address_space *mapping; const struct inode *inode; @@ -292,13 +292,13 @@ static bool bh_get_inode_and_lblk_num(const struct buffer_head *bh, * The ext4 journal (jbd2) can submit a buffer_head it directly created * for a non-pagecache page. fscrypt doesn't care about these. */ - mapping = page_mapping(page); + mapping = folio_mapping(folio); if (!mapping) return false; inode = mapping->host; *inode_ret = inode; - *lblk_num_ret = ((u64)page->index << (PAGE_SHIFT - inode->i_blkbits)) + + *lblk_num_ret = ((u64)folio->index << (PAGE_SHIFT - inode->i_blkbits)) + (bh_offset(bh) >> inode->i_blkbits); return true; } diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 0edf0b58daa7..787e9c8938ba 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -18,10 +18,11 @@ * information about these ioctls. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <crypto/skcipher.h> #include <linux/key-type.h> #include <linux/random.h> +#include <linux/once.h> #include <linux/seq_file.h> #include "fscrypt_private.h" @@ -74,8 +75,12 @@ void fscrypt_put_master_key(struct fscrypt_master_key *mk) * that concurrent keyring lookups can no longer find it. */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 0); - key_put(mk->mk_users); - mk->mk_users = NULL; + if (mk->mk_users) { + /* Clear the keyring so the quota gets released right away. */ + keyring_clear(mk->mk_users); + key_put(mk->mk_users); + mk->mk_users = NULL; + } call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); } diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index d71f7c799e79..b4fe01ea4bd4 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -23,7 +23,7 @@ struct fscrypt_mode fscrypt_modes[] = { .blk_crypto_mode = BLK_ENCRYPTION_MODE_AES_256_XTS, }, [FSCRYPT_MODE_AES_256_CTS] = { - .friendly_name = "AES-256-CTS-CBC", + .friendly_name = "AES-256-CBC-CTS", .cipher_str = "cts(cbc(aes))", .keysize = 32, .security_strength = 32, @@ -38,7 +38,7 @@ struct fscrypt_mode fscrypt_modes[] = { .blk_crypto_mode = BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV, }, [FSCRYPT_MODE_AES_128_CTS] = { - .friendly_name = "AES-128-CTS-CBC", + .friendly_name = "AES-128-CBC-CTS", .cipher_str = "cts(cbc(aes))", .keysize = 16, .security_strength = 16, @@ -53,7 +53,7 @@ struct fscrypt_mode fscrypt_modes[] = { .blk_crypto_mode = BLK_ENCRYPTION_MODE_SM4_XTS, }, [FSCRYPT_MODE_SM4_CTS] = { - .friendly_name = "SM4-CTS-CBC", + .friendly_name = "SM4-CBC-CTS", .cipher_str = "cts(cbc(sm4))", .keysize = 16, .security_strength = 16, @@ -687,7 +687,7 @@ int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported) /** * fscrypt_prepare_new_inode() - prepare to create a new inode in a directory * @dir: a possibly-encrypted directory - * @inode: the new inode. ->i_mode must be set already. + * @inode: the new inode. ->i_mode and ->i_blkbits must be set already. * ->i_ino doesn't need to be set yet. * @encrypt_ret: (output) set to %true if the new inode will be encrypted * @@ -717,6 +717,9 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode, if (IS_ERR(policy)) return PTR_ERR(policy); + if (WARN_ON_ONCE(inode->i_blkbits == 0)) + return -EINVAL; + if (WARN_ON_ONCE(inode->i_mode == 0)) return -EINVAL; |