From 3d463f28564618805713658c6aeb786fa23f420b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:52 -0800 Subject: fscrypt: move fscrypt_has_encryption_key() to supp/notsupp headers fscrypt_has_encryption_key() is already split into two versions depending on whether the filesystem is being built with encryption support or not. Move them into the appropriate headers. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 10 ---------- include/linux/fscrypt_notsupp.h | 5 +++++ include/linux/fscrypt_supp.h | 5 +++++ 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 08b4b40c5aa8..d1c891b5bd9c 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -129,11 +129,6 @@ static inline struct page *fscrypt_control_page(struct page *page) return ((struct fscrypt_ctx *)page_private(page))->w.control_page; } -static inline bool fscrypt_has_encryption_key(const struct inode *inode) -{ - return (inode->i_crypt_info != NULL); -} - #include #else /* !__FS_HAS_ENCRYPTION */ @@ -144,11 +139,6 @@ static inline struct page *fscrypt_control_page(struct page *page) return ERR_PTR(-EINVAL); } -static inline bool fscrypt_has_encryption_key(const struct inode *inode) -{ - return 0; -} - #include #endif /* __FS_HAS_ENCRYPTION */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 63e58808519a..52e330285457 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -14,6 +14,11 @@ #ifndef _LINUX_FSCRYPT_NOTSUPP_H #define _LINUX_FSCRYPT_NOTSUPP_H +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return false; +} + /* crypto.c */ static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index cf9e9fc02f0a..79bb8beae018 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -11,6 +11,11 @@ #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return (inode->i_crypt_info != NULL); +} + /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); -- cgit v1.2.3 From 4fd4b15ccbc79d512ad7982fc1a7ecd34703398f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:53 -0800 Subject: fscrypt: move fscrypt_control_page() to supp/notsupp headers fscrypt_control_page() is already split into two versions depending on whether the filesystem is being built with encryption support or not. Move them into the appropriate headers. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 18 ++---------------- include/linux/fscrypt_notsupp.h | 5 +++++ include/linux/fscrypt_supp.h | 6 ++++++ 3 files changed, 13 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index d1c891b5bd9c..c23b2f16129a 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -123,24 +123,10 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) } #if __FS_HAS_ENCRYPTION - -static inline struct page *fscrypt_control_page(struct page *page) -{ - return ((struct fscrypt_ctx *)page_private(page))->w.control_page; -} - #include - -#else /* !__FS_HAS_ENCRYPTION */ - -static inline struct page *fscrypt_control_page(struct page *page) -{ - WARN_ON_ONCE(1); - return ERR_PTR(-EINVAL); -} - +#else #include -#endif /* __FS_HAS_ENCRYPTION */ +#endif /** * fscrypt_require_key - require an inode's encryption key diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 52e330285457..812dc701a5b3 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -48,6 +48,11 @@ static inline int fscrypt_decrypt_page(const struct inode *inode, return -EOPNOTSUPP; } +static inline struct page *fscrypt_control_page(struct page *page) +{ + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +} static inline void fscrypt_restore_control_page(struct page *page) { diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 79bb8beae018..ad40780d4653 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -25,6 +25,12 @@ extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, u64, gfp_t); extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, unsigned int, u64); + +static inline struct page *fscrypt_control_page(struct page *page) +{ + return ((struct fscrypt_ctx *)page_private(page))->w.control_page; +} + extern void fscrypt_restore_control_page(struct page *); extern const struct dentry_operations fscrypt_d_ops; -- cgit v1.2.3 From e4de782a0960b9620c084881f12fb5e73ebff82b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:54 -0800 Subject: fscrypt: move fscrypt_info_cachep declaration to fscrypt_private.h The fscrypt_info kmem_cache is internal to fscrypt; filesystems don't need to access it. So move its declaration into fscrypt_private.h. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fscrypt_private.h | 1 + include/linux/fscrypt_supp.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index c0b4f5597e1a..823c43a00bf7 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -72,6 +72,7 @@ typedef enum { #define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002 /* crypto.c */ +extern struct kmem_cache *fscrypt_info_cachep; extern int fscrypt_initialize(unsigned int cop_flags); extern struct workqueue_struct *fscrypt_read_workqueue; extern int fscrypt_do_page_crypto(const struct inode *inode, diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index ad40780d4653..33d641e27c18 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -17,7 +17,6 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) } /* crypto.c */ -extern struct kmem_cache *fscrypt_info_cachep; extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); extern void fscrypt_release_ctx(struct fscrypt_ctx *); extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, -- cgit v1.2.3 From 542060c02cdb5c0740fd7156651463e321a859a3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:55 -0800 Subject: fscrypt: move fscrypt_ctx declaration to fscrypt_supp.h Filesystems only ever access 'struct fscrypt_ctx' through fscrypt functions. But when a filesystem is built without encryption support, these functions are all stubbed out, so the declaration of fscrypt_ctx is unneeded. Therefore, move it from fscrypt.h to fscrypt_supp.h. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 16 +--------------- include/linux/fscrypt_supp.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index c23b2f16129a..0f94d087a6d1 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -24,23 +24,9 @@ #define FS_CRYPTO_BLOCK_SIZE 16 +struct fscrypt_ctx; struct fscrypt_info; -struct fscrypt_ctx { - union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; - struct { - struct bio *bio; - struct work_struct work; - } r; - struct list_head free_list; /* Free list */ - }; - u8 flags; /* Flags */ -}; - /** * For encrypted symlinks, the ciphertext length is stored at the beginning * of the string in little-endian format. diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 33d641e27c18..fd6ee089ced0 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -11,6 +11,21 @@ #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H +struct fscrypt_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ +}; + static inline bool fscrypt_has_encryption_key(const struct inode *inode) { return (inode->i_crypt_info != NULL); -- cgit v1.2.3 From 1493651b53b4811960b6220a340929074b58a55b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:56 -0800 Subject: fscrypt: split fscrypt_dummy_context_enabled() into supp/notsupp versions fscrypt_dummy_context_enabled() accesses ->s_cop, which now is only set when the filesystem is built with encryption support. This didn't actually matter because no filesystems called it. However, it will start being used soon, so fix it by moving it from fscrypt.h to fscrypt_supp.h and stubbing it out in fscrypt_notsupp.h. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 8 -------- include/linux/fscrypt_notsupp.h | 5 +++++ include/linux/fscrypt_supp.h | 6 ++++++ 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 0f94d087a6d1..b671a4eef47f 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -75,14 +75,6 @@ struct fscrypt_operations { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 -static inline bool fscrypt_dummy_context_enabled(struct inode *inode) -{ - if (inode->i_sb->s_cop->dummy_context && - inode->i_sb->s_cop->dummy_context(inode)) - return true; - return false; -} - static inline bool fscrypt_valid_enc_modes(u32 contents_mode, u32 filenames_mode) { diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 812dc701a5b3..81e02201b215 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -19,6 +19,11 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) return false; } +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + return false; +} + /* crypto.c */ static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index fd6ee089ced0..e7dfa2974906 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -31,6 +31,12 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) return (inode->i_crypt_info != NULL); } +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + return inode->i_sb->s_cop->dummy_context && + inode->i_sb->s_cop->dummy_context(inode); +} + /* crypto.c */ extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); extern void fscrypt_release_ctx(struct fscrypt_ctx *); -- cgit v1.2.3 From bdd234764769a267794f275ce96706a466d376d7 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:57 -0800 Subject: fscrypt: move fscrypt_operations declaration to fscrypt_supp.h Filesystems now only define their fscrypt_operations when they are compiled with encryption support, so move the fscrypt_operations declaration from fscrypt.h to fscrypt_supp.h. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 18 ------------------ include/linux/fscrypt_supp.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index b671a4eef47f..33b95a91f720 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -54,24 +54,6 @@ struct fscrypt_name { #define fname_name(p) ((p)->disk_name.name) #define fname_len(p) ((p)->disk_name.len) -/* - * fscrypt superblock flags - */ -#define FS_CFLG_OWN_PAGES (1U << 1) - -/* - * crypto opertions for filesystems - */ -struct fscrypt_operations { - unsigned int flags; - const char *key_prefix; - int (*get_context)(struct inode *, void *, size_t); - int (*set_context)(struct inode *, const void *, size_t, void *); - bool (*dummy_context)(struct inode *); - bool (*empty_dir)(struct inode *); - unsigned (*max_namelen)(struct inode *); -}; - /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index e7dfa2974906..ce61caf26f40 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -11,6 +11,24 @@ #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H +/* + * fscrypt superblock flags + */ +#define FS_CFLG_OWN_PAGES (1U << 1) + +/* + * crypto operations for filesystems + */ +struct fscrypt_operations { + unsigned int flags; + const char *key_prefix; + int (*get_context)(struct inode *, void *, size_t); + int (*set_context)(struct inode *, const void *, size_t, void *); + bool (*dummy_context)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned (*max_namelen)(struct inode *); +}; + struct fscrypt_ctx { union { struct { -- cgit v1.2.3 From bb8179e5a8509876415c0eac6f6ba8a130b3cb47 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:58 -0800 Subject: fscrypt: move fscrypt_valid_enc_modes() to fscrypt_private.h The encryption modes are validated by fs/crypto/, not by individual filesystems. Therefore, move fscrypt_valid_enc_modes() from fscrypt.h to fscrypt_private.h. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fscrypt_private.h | 14 ++++++++++++++ include/linux/fscrypt.h | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 823c43a00bf7..2b848e7c92f0 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -71,6 +71,20 @@ typedef enum { #define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 #define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002 +static inline bool fscrypt_valid_enc_modes(u32 contents_mode, + u32 filenames_mode) +{ + if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && + filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) + return true; + + if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && + filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) + return true; + + return false; +} + /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern int fscrypt_initialize(unsigned int cop_flags); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 33b95a91f720..2e4dce0365cf 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -57,20 +57,6 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 -static inline bool fscrypt_valid_enc_modes(u32 contents_mode, - u32 filenames_mode) -{ - if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && - filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) - return true; - - if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && - filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) - return true; - - return false; -} - static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) { if (str->len == 1 && str->name[0] == '.') -- cgit v1.2.3 From dcf0db9e5df369461c9d55282abbf66d263ef2db Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:44:59 -0800 Subject: fscrypt: move fscrypt_is_dot_dotdot() to fs/crypto/fname.c Only fs/crypto/fname.c cares about treating the "." and ".." filenames specially with regards to encryption, so move fscrypt_is_dot_dotdot() from fscrypt.h to there. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 11 +++++++++++ include/linux/fscrypt.h | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 305541bcd108..b8c5061553b1 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -15,6 +15,17 @@ #include #include "fscrypt_private.h" +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; +} + /** * fname_encrypt() - encrypt a filename * diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 2e4dce0365cf..3045fc49d3ca 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -57,17 +57,6 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 -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; -} - #if __FS_HAS_ENCRYPTION #include #else -- cgit v1.2.3 From a575784c6c13b8f1bae05fbba873e326ec73e289 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:45:00 -0800 Subject: fscrypt: trim down fscrypt.h includes fscrypt.h included way too many other headers, given that it is included by filesystems both with and without encryption support. Trim down the includes list by moving the needed includes into more appropriate places, and removing the unneeded ones. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/crypto.c | 1 + fs/crypto/fname.c | 1 + fs/crypto/keyinfo.c | 1 + include/linux/fscrypt.h | 6 ------ include/linux/fscrypt_supp.h | 3 +++ 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 732a786cce9d..ce654526c0fb 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "fscrypt_private.h" static unsigned int num_prealloc_crypto_pages = 32; diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index b8c5061553b1..52d4dbe1e8e7 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -13,6 +13,7 @@ #include #include +#include #include "fscrypt_private.h" static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 5e6e846f5a24..c115eac4b4cf 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "fscrypt_private.h" static struct crypto_shash *essiv_hash_tfm; diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 3045fc49d3ca..071ebabfc287 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -14,13 +14,7 @@ #ifndef _LINUX_FSCRYPT_H #define _LINUX_FSCRYPT_H -#include #include -#include -#include -#include -#include -#include #define FS_CRYPTO_BLOCK_SIZE 16 diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index ce61caf26f40..562a9bc04560 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -11,6 +11,9 @@ #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H +#include +#include + /* * fscrypt superblock flags */ -- cgit v1.2.3 From 76e81d6d50481144824237e6843122824b0a55c0 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:45:01 -0800 Subject: fscrypt: new helper functions for ->symlink() Currently, filesystems supporting fscrypt need to implement some tricky logic when creating encrypted symlinks, including handling a peculiar on-disk format (struct fscrypt_symlink_data) and correctly calculating the size of the encrypted symlink. Introduce helper functions to make things a bit easier: - fscrypt_prepare_symlink() computes and validates the size the symlink target will require on-disk. - fscrypt_encrypt_symlink() creates the encrypted target if needed. The new helpers actually fix some subtle bugs. First, when checking whether the symlink target was too long, filesystems didn't account for the fact that the NUL padding is meant to be truncated if it would cause the maximum length to be exceeded, as is done for filenames in directories. Consequently users would receive ENAMETOOLONG when creating symlinks close to what is supposed to be the maximum length. For example, with EXT4 with a 4K block size, the maximum symlink target length in an encrypted directory is supposed to be 4093 bytes (in comparison to 4095 in an unencrypted directory), but in FS_POLICY_FLAGS_PAD_32-mode only up to 4064 bytes were accepted. Second, symlink targets of "." and ".." were not being encrypted, even though they should be, as these names are special in *directory entries* but not in symlink targets. Fortunately, we can fix this simply by starting to encrypt them, as old kernels already accept them in encrypted form. Third, the output string length the filesystems were providing when doing the actual encryption was incorrect, as it was forgotten to exclude 'sizeof(struct fscrypt_symlink_data)'. Fortunately though, this bug didn't make a difference. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 8 ++-- fs/crypto/fscrypt_private.h | 4 ++ fs/crypto/hooks.c | 90 +++++++++++++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 64 +++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 16 ++++++++ include/linux/fscrypt_supp.h | 6 +++ 6 files changed, 185 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 52d4dbe1e8e7..62f13d533439 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -34,8 +34,8 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) * * Return: 0 on success, -errno on failure */ -static int fname_encrypt(struct inode *inode, - const struct qstr *iname, struct fscrypt_str *oname) +int fname_encrypt(struct inode *inode, + const struct qstr *iname, struct fscrypt_str *oname) { struct skcipher_request *req = NULL; DECLARE_CRYPTO_WAIT(wait); @@ -56,9 +56,11 @@ static int fname_encrypt(struct inode *inode, * Copy the filename to the output buffer for encrypting in-place and * pad it with the needed number of NUL bytes. */ + if (WARN_ON(oname->len < iname->len)) + return -ENOBUFS; cryptlen = max_t(unsigned int, iname->len, FS_CRYPTO_BLOCK_SIZE); cryptlen = round_up(cryptlen, padding); - cryptlen = min(cryptlen, lim); + cryptlen = min3(cryptlen, lim, oname->len); memcpy(oname->name, iname->name, iname->len); memset(oname->name + iname->len, 0, cryptlen - iname->len); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 2b848e7c92f0..6995bca5006b 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -98,6 +98,10 @@ extern int fscrypt_do_page_crypto(const struct inode *inode, extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags); +/* fname.c */ +extern int fname_encrypt(struct inode *inode, + const struct qstr *iname, struct fscrypt_str *oname); + /* keyinfo.c */ extern void __exit fscrypt_essiv_cleanup(void); diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 9f5fb2eb9cf7..4b83e4af2e41 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -110,3 +110,93 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry) return 0; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); + +int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, + unsigned int max_len, + struct fscrypt_str *disk_link) +{ + int err; + + /* + * To calculate the size of the encrypted symlink target we need to know + * the amount of NUL padding, which is determined by the flags set in + * the encryption policy which will be inherited from the directory. + * The easiest way to get access to this is to just load the directory's + * fscrypt_info, since we'll need it to create the dir_entry anyway. + * + * Note: in test_dummy_encryption mode, @dir may be unencrypted. + */ + err = fscrypt_get_encryption_info(dir); + if (err) + return err; + if (!fscrypt_has_encryption_key(dir)) + return -ENOKEY; + + /* + * Calculate the size of the encrypted symlink and verify it won't + * exceed max_len. Note that for historical reasons, encrypted symlink + * targets are prefixed with the ciphertext length, despite this + * actually being redundant with i_size. This decreases by 2 bytes the + * longest symlink target we can accept. + * + * We could recover 1 byte by not counting a null terminator, but + * counting it (even though it is meaningless for ciphertext) is simpler + * for now since filesystems will assume it is there and subtract it. + */ + if (sizeof(struct fscrypt_symlink_data) + len > max_len) + return -ENAMETOOLONG; + disk_link->len = min_t(unsigned int, + sizeof(struct fscrypt_symlink_data) + + fscrypt_fname_encrypted_size(dir, len), + max_len); + disk_link->name = NULL; + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_symlink); + +int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, + unsigned int len, struct fscrypt_str *disk_link) +{ + int err; + struct qstr iname = { .name = target, .len = len }; + struct fscrypt_symlink_data *sd; + unsigned int ciphertext_len; + struct fscrypt_str oname; + + err = fscrypt_require_key(inode); + if (err) + return err; + + if (disk_link->name) { + /* filesystem-provided buffer */ + sd = (struct fscrypt_symlink_data *)disk_link->name; + } else { + sd = kmalloc(disk_link->len, GFP_NOFS); + if (!sd) + return -ENOMEM; + } + ciphertext_len = disk_link->len - sizeof(*sd); + sd->len = cpu_to_le16(ciphertext_len); + + oname.name = sd->encrypted_path; + oname.len = ciphertext_len; + err = fname_encrypt(inode, &iname, &oname); + if (err) { + if (!disk_link->name) + kfree(sd); + return err; + } + BUG_ON(oname.len != ciphertext_len); + + /* + * Null-terminating the ciphertext doesn't make sense, but we still + * count the null terminator in the length, so we might as well + * initialize it just in case the filesystem writes it out. + */ + sd->encrypted_path[ciphertext_len] = '\0'; + + if (!disk_link->name) + disk_link->name = (unsigned char *)sd; + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 071ebabfc287..6a678d0e956a 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -196,4 +196,68 @@ static inline int fscrypt_prepare_setattr(struct dentry *dentry, return 0; } +/** + * fscrypt_prepare_symlink - prepare to create a possibly-encrypted symlink + * @dir: directory in which the symlink is being created + * @target: plaintext symlink target + * @len: length of @target excluding null terminator + * @max_len: space the filesystem has available to store the symlink target + * @disk_link: (out) the on-disk symlink target being prepared + * + * This function computes the size the symlink target will require on-disk, + * stores it in @disk_link->len, and validates it against @max_len. An + * encrypted symlink may be longer than the original. + * + * Additionally, @disk_link->name is set to @target if the symlink will be + * unencrypted, but left NULL if the symlink will be encrypted. For encrypted + * symlinks, the filesystem must call fscrypt_encrypt_symlink() to create the + * on-disk target later. (The reason for the two-step process is that some + * filesystems need to know the size of the symlink target before creating the + * inode, e.g. to determine whether it will be a "fast" or "slow" symlink.) + * + * Return: 0 on success, -ENAMETOOLONG if the symlink target is too long, + * -ENOKEY if the encryption key is missing, or another -errno code if a problem + * occurred while setting up the encryption key. + */ +static inline int fscrypt_prepare_symlink(struct inode *dir, + const char *target, + unsigned int len, + unsigned int max_len, + struct fscrypt_str *disk_link) +{ + if (IS_ENCRYPTED(dir) || fscrypt_dummy_context_enabled(dir)) + return __fscrypt_prepare_symlink(dir, len, max_len, disk_link); + + disk_link->name = (unsigned char *)target; + disk_link->len = len + 1; + if (disk_link->len > max_len) + return -ENAMETOOLONG; + return 0; +} + +/** + * fscrypt_encrypt_symlink - encrypt the symlink target if needed + * @inode: symlink inode + * @target: plaintext symlink target + * @len: length of @target excluding null terminator + * @disk_link: (in/out) the on-disk symlink target being prepared + * + * If the symlink target needs to be encrypted, then this function encrypts it + * into @disk_link->name. fscrypt_prepare_symlink() must have been called + * previously to compute @disk_link->len. If the filesystem did not allocate a + * buffer for @disk_link->name after calling fscrypt_prepare_link(), then one + * will be kmalloc()'ed and the filesystem will be responsible for freeing it. + * + * Return: 0 on success, -errno on failure + */ +static inline int fscrypt_encrypt_symlink(struct inode *inode, + const char *target, + unsigned int len, + struct fscrypt_str *disk_link) +{ + if (IS_ENCRYPTED(inode)) + return __fscrypt_encrypt_symlink(inode, target, len, disk_link); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 81e02201b215..02ec0aa894d8 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -223,4 +223,20 @@ static inline int __fscrypt_prepare_lookup(struct inode *dir, return -EOPNOTSUPP; } +static inline int __fscrypt_prepare_symlink(struct inode *dir, + unsigned int len, + unsigned int max_len, + struct fscrypt_str *disk_link) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_encrypt_symlink(struct inode *inode, + const char *target, + unsigned int len, + struct fscrypt_str *disk_link) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 562a9bc04560..7e0b67ccd816 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -205,5 +205,11 @@ extern int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *new_dentry, unsigned int flags); extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry); +extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, + unsigned int max_len, + struct fscrypt_str *disk_link); +extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, + unsigned int len, + struct fscrypt_str *disk_link); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- cgit v1.2.3 From 3b0d8837a79ba7b7cc324d1f2b206c074e9c6182 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 5 Jan 2018 10:45:02 -0800 Subject: fscrypt: new helper function - fscrypt_get_symlink() Filesystems also have duplicate code to support ->get_link() on encrypted symlinks. Factor it out into a new function fscrypt_get_symlink(). It takes in the contents of the encrypted symlink on-disk and provides the target (decrypted or encoded) that should be returned from ->get_link(). Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 73 +++++++++++++++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 8 +++++ include/linux/fscrypt_supp.h | 3 ++ 3 files changed, 84 insertions(+) (limited to 'include') diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 4b83e4af2e41..8900e348ba6e 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -200,3 +200,76 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, return 0; } EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); + +/** + * fscrypt_get_symlink - get the target of an encrypted symlink + * @inode: the symlink inode + * @caddr: the on-disk contents of the symlink + * @max_size: size of @caddr buffer + * @done: if successful, will be set up to free the returned target + * + * If the symlink's encryption key is available, we decrypt its target. + * Otherwise, we encode its target for presentation. + * + * This may sleep, so the filesystem must have dropped out of RCU mode already. + * + * Return: the presentable symlink target or an ERR_PTR() + */ +const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, + unsigned int max_size, + struct delayed_call *done) +{ + const struct fscrypt_symlink_data *sd; + struct fscrypt_str cstr, pstr; + int err; + + /* This is for encrypted symlinks only */ + if (WARN_ON(!IS_ENCRYPTED(inode))) + return ERR_PTR(-EINVAL); + + /* + * Try to set up the symlink's encryption key, but we can continue + * regardless of whether the key is available or not. + */ + err = fscrypt_get_encryption_info(inode); + if (err) + return ERR_PTR(err); + + /* + * For historical reasons, encrypted symlink targets are prefixed with + * the ciphertext length, even though this is redundant with i_size. + */ + + if (max_size < sizeof(*sd)) + return ERR_PTR(-EUCLEAN); + sd = caddr; + cstr.name = (unsigned char *)sd->encrypted_path; + cstr.len = le16_to_cpu(sd->len); + + if (cstr.len == 0) + return ERR_PTR(-EUCLEAN); + + if (cstr.len + sizeof(*sd) - 1 > max_size) + return ERR_PTR(-EUCLEAN); + + err = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr); + if (err) + return ERR_PTR(err); + + err = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr); + if (err) + goto err_kfree; + + err = -EUCLEAN; + if (pstr.name[0] == '\0') + goto err_kfree; + + pstr.name[pstr.len] = '\0'; + set_delayed_call(done, kfree_link, pstr.name); + return pstr.name; + +err_kfree: + kfree(pstr.name); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(fscrypt_get_symlink); diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 02ec0aa894d8..dd106640c6ea 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -239,4 +239,12 @@ static inline int __fscrypt_encrypt_symlink(struct inode *inode, return -EOPNOTSUPP; } +static inline const char *fscrypt_get_symlink(struct inode *inode, + const void *caddr, + unsigned int max_size, + struct delayed_call *done) +{ + return ERR_PTR(-EOPNOTSUPP); +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 7e0b67ccd816..dc2babf3f7d3 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -211,5 +211,8 @@ extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, unsigned int len, struct fscrypt_str *disk_link); +extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, + unsigned int max_size, + struct delayed_call *done); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- cgit v1.2.3 From 1e80ad712f69a34d1e3c556d1d674d04b266ad40 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 11 Jan 2018 23:30:08 -0500 Subject: fscrypt: remove fscrypt_fname_usr_to_disk() fscrypt_fname_usr_to_disk() sounded very generic but was actually only used to encrypt symlinks. Remove it now that all filesystems have been switched over to fscrypt_encrypt_symlink(). Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 29 ----------------------------- include/linux/fscrypt_notsupp.h | 7 ------- include/linux/fscrypt_supp.h | 2 -- 3 files changed, 38 deletions(-) (limited to 'include') diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 62f13d533439..cae7d1bcc8c5 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -311,35 +311,6 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, } EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); -/** - * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk - * space - * - * The caller must have allocated sufficient memory for the @oname string. - * - * Return: 0 on success, -errno on failure - */ -int fscrypt_fname_usr_to_disk(struct inode *inode, - const struct qstr *iname, - struct fscrypt_str *oname) -{ - if (fscrypt_is_dot_dotdot(iname)) { - oname->name[0] = '.'; - oname->name[iname->len - 1] = '.'; - oname->len = iname->len; - return 0; - } - if (inode->i_crypt_info) - return fname_encrypt(inode, iname, oname); - /* - * Without a proper key, a user is not allowed to modify the filenames - * in a directory. Consequently, a user space name cannot be mapped to - * a disk-space name - */ - return -ENOKEY; -} -EXPORT_SYMBOL(fscrypt_fname_usr_to_disk); - /** * fscrypt_setup_filename() - prepare to search a possibly encrypted directory * @dir: the directory that will be searched diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index dd106640c6ea..0962f504aa91 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -159,13 +159,6 @@ static inline int fscrypt_fname_disk_to_usr(struct inode *inode, return -EOPNOTSUPP; } -static inline int fscrypt_fname_usr_to_disk(struct inode *inode, - const struct qstr *iname, - struct fscrypt_str *oname) -{ - return -EOPNOTSUPP; -} - static inline bool fscrypt_match_name(const struct fscrypt_name *fname, const u8 *de_name, u32 de_name_len) { diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index dc2babf3f7d3..e00191deb0d6 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -113,8 +113,6 @@ extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, extern void fscrypt_fname_free_buffer(struct fscrypt_str *); extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, const struct fscrypt_str *, struct fscrypt_str *); -extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, - struct fscrypt_str *); #define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 -- cgit v1.2.3 From 0eaab5b10621e84868df911dad43d330fa1b9bc8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 11 Jan 2018 23:30:08 -0500 Subject: fscrypt: move fscrypt_symlink_data to fscrypt_private.h Now that all filesystems have been converted to use the symlink helper functions, they no longer need the declaration of 'struct fscrypt_symlink_data'. Move it from fscrypt.h to fscrypt_private.h. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fscrypt_private.h | 9 +++++++++ include/linux/fscrypt.h | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 6995bca5006b..cec9ce309f41 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -50,6 +50,15 @@ struct fscrypt_context { #define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct fscrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __packed; + /* * A pointer to this structure is stored in the file system's in-core * representation of an inode. diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 6a678d0e956a..952ab97af325 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -21,15 +21,6 @@ struct fscrypt_ctx; struct fscrypt_info; -/** - * For encrypted symlinks, the ciphertext length is stored at the beginning - * of the string in little-endian format. - */ -struct fscrypt_symlink_data { - __le16 len; - char encrypted_path[1]; -} __packed; - struct fscrypt_str { unsigned char *name; u32 len; -- cgit v1.2.3 From 2cbadadcfdf0d8a538ce32ed12e18ef487773b07 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 11 Jan 2018 23:30:08 -0500 Subject: fscrypt: define fscrypt_fname_alloc_buffer() to be for presented names Previously fscrypt_fname_alloc_buffer() was used to allocate buffers for both presented (decrypted or encoded) and encrypted filenames. That was confusing, because it had to allocate the worst-case size for either, e.g. including NUL-padding even when it was meaningless. But now that fscrypt_setup_filename() no longer calls it, it is only used in the ->get_link() and ->readdir() paths, which specifically want a buffer for presented filenames. Therefore, switch the behavior over to allocating the buffer for presented filenames only. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 29 ++++++++++++++--------------- include/linux/fscrypt_notsupp.h | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 65424b89a1d1..44ddd094b7c5 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -204,37 +204,36 @@ u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen) EXPORT_SYMBOL(fscrypt_fname_encrypted_size); /** - * fscrypt_fname_crypto_alloc_obuff() - + * fscrypt_fname_alloc_buffer - allocate a buffer for presented filenames * - * Allocates an output buffer that is sufficient for the crypto operation - * specified by the context and the direction. + * Allocate a buffer that is large enough to hold any decrypted or encoded + * filename (null-terminated), for the given maximum encrypted filename length. + * + * Return: 0 on success, -errno on failure */ int fscrypt_fname_alloc_buffer(const struct inode *inode, - u32 ilen, struct fscrypt_str *crypto_str) + u32 max_encrypted_len, + struct fscrypt_str *crypto_str) { - u32 olen = fscrypt_fname_encrypted_size(inode, ilen); const u32 max_encoded_len = max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE), 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))); + u32 max_presented_len; - crypto_str->len = olen; - olen = max(olen, max_encoded_len); + max_presented_len = max(max_encoded_len, max_encrypted_len); - /* - * Allocated buffer can hold one more character to null-terminate the - * string - */ - crypto_str->name = kmalloc(olen + 1, GFP_NOFS); - if (!(crypto_str->name)) + crypto_str->name = kmalloc(max_presented_len + 1, GFP_NOFS); + if (!crypto_str->name) return -ENOMEM; + crypto_str->len = max_presented_len; return 0; } EXPORT_SYMBOL(fscrypt_fname_alloc_buffer); /** - * fscrypt_fname_crypto_free_buffer() - + * fscrypt_fname_free_buffer - free the buffer for presented filenames * - * Frees the buffer allocated for crypto operation. + * Free the buffer allocated by fscrypt_fname_alloc_buffer(). */ void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str) { diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 0962f504aa91..c9592e307df5 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -140,7 +140,7 @@ static inline u32 fscrypt_fname_encrypted_size(const struct inode *inode, } static inline int fscrypt_fname_alloc_buffer(const struct inode *inode, - u32 ilen, + u32 max_encrypted_len, struct fscrypt_str *crypto_str) { return -EOPNOTSUPP; -- cgit v1.2.3 From b9db0b4a68d373897119d1e0a3beebe1c1a23936 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 11 Jan 2018 23:30:08 -0500 Subject: fscrypt: fix up fscrypt_fname_encrypted_size() for internal use Filesystems don't need fscrypt_fname_encrypted_size() anymore, so unexport it and move it to fscrypt_private.h. We also never calculate the encrypted size of a filename without having the fscrypt_info present since it is needed to know the amount of NUL-padding which is determined by the encryption policy, and also we will always truncate the NUL-padding to the maximum filename length. Therefore, also make fscrypt_fname_encrypted_size() assume that the fscrypt_info is present, and make it truncate the returned length to the specified max_len. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 31 +++++++++++++++---------------- fs/crypto/fscrypt_private.h | 3 +++ fs/crypto/hooks.c | 10 +++++----- include/linux/fscrypt_notsupp.h | 8 -------- include/linux/fscrypt_supp.h | 1 - 5 files changed, 23 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 44ddd094b7c5..e33f3d3c5ade 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -191,17 +191,20 @@ static int digest_decode(const char *src, int len, char *dst) return cp - dst; } -u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen) +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret) { - int padding = 32; - struct fscrypt_info *ci = inode->i_crypt_info; - - if (ci) - padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); - ilen = max(ilen, (u32)FS_CRYPTO_BLOCK_SIZE); - return round_up(ilen, padding); + int padding = 4 << (inode->i_crypt_info->ci_flags & + FS_POLICY_FLAGS_PAD_MASK); + u32 encrypted_len; + + if (orig_len > max_len) + return false; + encrypted_len = max(orig_len, (u32)FS_CRYPTO_BLOCK_SIZE); + encrypted_len = round_up(encrypted_len, padding); + *encrypted_len_ret = min(encrypted_len, max_len); + return true; } -EXPORT_SYMBOL(fscrypt_fname_encrypted_size); /** * fscrypt_fname_alloc_buffer - allocate a buffer for presented filenames @@ -342,14 +345,10 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, return ret; if (dir->i_crypt_info) { - unsigned int max_len = dir->i_sb->s_cop->max_namelen(dir); - - if (iname->len > max_len) + if (!fscrypt_fname_encrypted_size(dir, iname->len, + dir->i_sb->s_cop->max_namelen(dir), + &fname->crypto_buf.len)) return -ENAMETOOLONG; - - fname->crypto_buf.len = - min(fscrypt_fname_encrypted_size(dir, iname->len), - max_len); fname->crypto_buf.name = kmalloc(fname->crypto_buf.len, GFP_NOFS); if (!fname->crypto_buf.name) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 053917587200..ad6722bae8b7 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -110,6 +110,9 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, /* fname.c */ extern int fname_encrypt(struct inode *inode, const struct qstr *iname, u8 *out, unsigned int olen); +extern bool fscrypt_fname_encrypted_size(const struct inode *inode, + u32 orig_len, u32 max_len, + u32 *encrypted_len_ret); /* keyinfo.c */ extern void __exit fscrypt_essiv_cleanup(void); diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 5bf38d94c5d2..28f9f059571d 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -143,12 +143,12 @@ int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, * counting it (even though it is meaningless for ciphertext) is simpler * for now since filesystems will assume it is there and subtract it. */ - if (sizeof(struct fscrypt_symlink_data) + len > max_len) + if (!fscrypt_fname_encrypted_size(dir, len, + max_len - sizeof(struct fscrypt_symlink_data), + &disk_link->len)) return -ENAMETOOLONG; - disk_link->len = min_t(unsigned int, - sizeof(struct fscrypt_symlink_data) + - fscrypt_fname_encrypted_size(dir, len), - max_len); + disk_link->len += sizeof(struct fscrypt_symlink_data); + disk_link->name = NULL; return 0; } diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index c9592e307df5..342eb97e0476 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -131,14 +131,6 @@ static inline void fscrypt_free_filename(struct fscrypt_name *fname) return; } -static inline u32 fscrypt_fname_encrypted_size(const struct inode *inode, - u32 ilen) -{ - /* never happens */ - WARN_ON(1); - return 0; -} - static inline int fscrypt_fname_alloc_buffer(const struct inode *inode, u32 max_encrypted_len, struct fscrypt_str *crypto_str) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index e00191deb0d6..2dd5767c77b0 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -107,7 +107,6 @@ static inline void fscrypt_free_filename(struct fscrypt_name *fname) kfree(fname->crypto_buf.name); } -extern u32 fscrypt_fname_encrypted_size(const struct inode *, u32); extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, struct fscrypt_str *); extern void fscrypt_fname_free_buffer(struct fscrypt_str *); -- cgit v1.2.3 From 3d204e24d452f96704f5feb83f6b7654245defc9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 11 Jan 2018 23:30:13 -0500 Subject: fscrypt: remove 'ci' parameter from fscrypt_put_encryption_info() fscrypt_put_encryption_info() is only called when evicting an inode, so the 'struct fscrypt_info *ci' parameter is always NULL, and there cannot be races with other threads. This was cruft left over from the broken key revocation code. Remove the unused parameter and the cmpxchg(). Also remove the #ifdefs around the fscrypt_put_encryption_info() calls, since fscrypt_notsupp.h defines a no-op stub for it. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/keyinfo.c | 16 +++------------- fs/ext4/super.c | 4 +--- fs/f2fs/inode.c | 2 +- fs/ubifs/super.c | 4 +--- include/linux/fscrypt_notsupp.h | 3 +-- include/linux/fscrypt_supp.h | 2 +- 6 files changed, 8 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index c115eac4b4cf..05f5ee1f0705 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -355,19 +355,9 @@ out: } EXPORT_SYMBOL(fscrypt_get_encryption_info); -void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci) +void fscrypt_put_encryption_info(struct inode *inode) { - struct fscrypt_info *prev; - - if (ci == NULL) - ci = READ_ONCE(inode->i_crypt_info); - if (ci == NULL) - return; - - prev = cmpxchg(&inode->i_crypt_info, ci, NULL); - if (prev != ci) - return; - - put_crypt_info(ci); + put_crypt_info(inode->i_crypt_info); + inode->i_crypt_info = NULL; } EXPORT_SYMBOL(fscrypt_put_encryption_info); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 7c46693a14d7..152d05d983f6 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1069,9 +1069,7 @@ void ext4_clear_inode(struct inode *inode) jbd2_free_inode(EXT4_I(inode)->jinode); EXT4_I(inode)->jinode = NULL; } -#ifdef CONFIG_EXT4_FS_ENCRYPTION - fscrypt_put_encryption_info(inode, NULL); -#endif + fscrypt_put_encryption_info(inode); } static struct inode *ext4_nfs_get_inode(struct super_block *sb, diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index b4c4f2b25304..22a5607a0518 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -569,7 +569,7 @@ no_delete: !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); } out_clear: - fscrypt_put_encryption_info(inode, NULL); + fscrypt_put_encryption_info(inode); clear_inode(inode); } diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 0beb285b143d..b16ef162344a 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -379,9 +379,7 @@ out: } done: clear_inode(inode); -#ifdef CONFIG_UBIFS_FS_ENCRYPTION - fscrypt_put_encryption_info(inode, NULL); -#endif + fscrypt_put_encryption_info(inode); } static void ubifs_dirty_inode(struct inode *inode, int flags) diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 342eb97e0476..44b50c04bae9 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -105,8 +105,7 @@ static inline int fscrypt_get_encryption_info(struct inode *inode) return -EOPNOTSUPP; } -static inline void fscrypt_put_encryption_info(struct inode *inode, - struct fscrypt_info *ci) +static inline void fscrypt_put_encryption_info(struct inode *inode) { return; } diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 2dd5767c77b0..477a7a6504d2 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -96,7 +96,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *, void *, bool); /* keyinfo.c */ extern int fscrypt_get_encryption_info(struct inode *); -extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); +extern void fscrypt_put_encryption_info(struct inode *); /* fname.c */ extern int fscrypt_setup_filename(struct inode *, const struct qstr *, -- cgit v1.2.3