From patchwork Sat Sep 2 05:54:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373019 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D7DACA0FFF for ; Sat, 2 Sep 2023 05:55:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351571AbjIBFzw (ORCPT ); Sat, 2 Sep 2023 01:55:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41060 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231132AbjIBFzv (ORCPT ); Sat, 2 Sep 2023 01:55:51 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CA47610F5; Fri, 1 Sep 2023 22:55:46 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id E801F803B8; Sat, 2 Sep 2023 01:55:45 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634146; bh=kxcFIEA+Tz7C5XSXfEIV9jwTiWVBlr8UrdIiPo4CwNM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=u6URqt6LxmS6/sFtcmFMkwTXguzyuzsyA2Ntaf+SZEygOWe5UkHDHpGNvYZvx064M oo6QPoLcnO3WCuu9hadAA3ehzwmE50rPjUgV3HHSHZRzlOMD6YAK6FP3DCU2JgnR5b gwJfM2zHmHPP0n/EwJ68c3S+m1PKGGd8xW1dsqPzJ6nvCWZeWV4p811ZDLdUsCzY5g y/gdLHjKEWGbJqAXxzoADRiItr3bsaTFKzWH6eZjFVlL/PoEfkw24CG6l2gBkWOLGn 1BHLTohN3b/plTqArm4gAE76TwmzXhUzh0yt4LY3TCZrFoZNq1VM1UGOfb4yfsQ/e5 i9v+d3ADSvA2g== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 01/13] fscrypt: factor getting info for a specific block Date: Sat, 2 Sep 2023 01:54:19 -0400 Message-ID: <3f70553c0c8fe344aaa5e9b6c54c0267c26d2d7b.1693630890.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org For filesystems using extent-based encryption, the content of each extent will be encrypted with a different fscrypt_info for each extent. Meanwhile, directories and symlinks will continue to use the fscrypt_info for the inode. Therefore, merely grabbing inode->i_crypt_info will be insufficient; the caller must specifically request the inode info or the info for a specific block. Add fscrypt_get_lblk_info() to get info for a specific block, and update all relevant callsites. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 3 ++- fs/crypto/fscrypt_private.h | 29 +++++++++++++++++++++++++++++ fs/crypto/inline_crypt.c | 10 ++++++---- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 9f3bda18c797..1b7e375b1c6b 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -107,7 +107,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, struct skcipher_request *req = NULL; DECLARE_CRYPTO_WAIT(wait); struct scatterlist dst, src; - struct fscrypt_info *ci = inode->i_crypt_info; + struct fscrypt_info *ci = + fscrypt_get_lblk_info(inode, lblk_num, NULL, NULL); struct crypto_skcipher *tfm = ci->ci_enc_key->tfm; int res = 0; diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index e2acd8894ea7..8a1fd1d33cfc 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -277,6 +277,35 @@ typedef enum { FS_ENCRYPT, } fscrypt_direction_t; +/** + * fscrypt_get_lblk_info() - get the fscrypt_info to crypt a particular block + * + * @inode: the inode to which the block belongs + * @lblk: the offset of the block within the file which the inode + * references + * @offset: a pointer to return the offset of the block from the first block + * that the info covers. For inode-based encryption, this will + * always be @lblk; for extent-based encryption, this will be in + * the range [0, lblk]. Can be NULL + * @extent_len: a pointer to return the minimum number of lblks starting at + * this offset which also belong to the same fscrypt_info. Can be + * NULL + * + * Return: the appropriate fscrypt_info if there is one, else NULL. + */ +static inline struct fscrypt_info * +fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset, + u64 *extent_len) +{ + if (offset) + *offset = lblk; + if (extent_len) + *extent_len = U64_MAX; + + return inode->i_crypt_info; +} + + /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; int fscrypt_initialize(struct super_block *sb); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 2063f7941ce6..885a2ec3d711 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -270,7 +270,7 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, if (!fscrypt_inode_uses_inline_crypto(inode)) return; - ci = inode->i_crypt_info; + ci = fscrypt_get_lblk_info(inode, first_lblk, NULL, NULL); fscrypt_generate_dun(ci, first_lblk, dun); bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask); @@ -349,21 +349,23 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, { const struct bio_crypt_ctx *bc = bio->bi_crypt_context; u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; + struct fscrypt_info *ci; if (!!bc != fscrypt_inode_uses_inline_crypto(inode)) return false; if (!bc) return true; + ci = fscrypt_get_lblk_info(inode, next_lblk, NULL, NULL); /* * Comparing the key pointers is good enough, as all I/O for each key * uses the same pointer. I.e., there's currently no need to support * merging requests where the keys are the same but the pointers differ. */ - if (bc->bc_key != inode->i_crypt_info->ci_enc_key->blk_key) + if (bc->bc_key != ci->ci_enc_key->blk_key) return false; - fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun); + fscrypt_generate_dun(ci, next_lblk, next_dun); return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun); } EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); @@ -465,7 +467,7 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) if (nr_blocks <= 1) return nr_blocks; - ci = inode->i_crypt_info; + ci = fscrypt_get_lblk_info(inode, lblk, NULL, NULL); if (!(fscrypt_policy_flags(&ci->ci_policy) & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) return nr_blocks; From patchwork Sat Sep 2 05:54:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373020 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6579CCA0FFE for ; Sat, 2 Sep 2023 05:55:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351582AbjIBFzw (ORCPT ); Sat, 2 Sep 2023 01:55:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32864 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343615AbjIBFzv (ORCPT ); Sat, 2 Sep 2023 01:55:51 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5DAF10F4; Fri, 1 Sep 2023 22:55:48 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id EC49C8057C; Sat, 2 Sep 2023 01:55:47 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634148; bh=EZsG1gGgKChupzzFm3iQhX2nJ7xoA/ZL78TwI1gPkn0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GBTd/wIF7d1AdA/0AharhgWfQnUAvKKYixKzwYNjNCieR7EX0QLpOf+u5SfMCekP5 3G19CNxaSqt+LFhtHbXG0CuQpBfFDOv7rAKsvJqTdJFLlXfCHdx/KTBGlHKqn/yqyp YKvmb8ut2cjh+QQNRO+dni0ZF46n40F3AcgtWreUAGgj8fv3xvD2//+v4323uT5Q/0 xSe+sBr8vXoBmVTXHWSJUeaI42Kc1+OeC094Vytf+d7ld17eNEr0BwHk713jrlD4Fz k7aTnIpjD86GX03K8vOE5jDeyTijZQl/FCeTOTZGSadAi2TujdXfIGsWeYFUT2f023 aB8FJSown6kUg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 02/13] fscrypt: adjust effective lblks based on extents Date: Sat, 2 Sep 2023 01:54:20 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org If a filesystem uses extent-based encryption, then the offset within a file is not a constant which can be used for calculating an IV. For instance, the same extent could be blocks 0-8 in one file, and blocks 100-108 in another file. Instead, the block offset within the extent must be used instead. Update all uses of logical block offset within the file to use logical block offset within the extent, if applicable. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 3 ++- fs/crypto/inline_crypt.c | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 1b7e375b1c6b..d75f1b3f5795 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -107,8 +107,9 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, struct skcipher_request *req = NULL; DECLARE_CRYPTO_WAIT(wait); struct scatterlist dst, src; + u64 ci_offset = 0; struct fscrypt_info *ci = - fscrypt_get_lblk_info(inode, lblk_num, NULL, NULL); + fscrypt_get_lblk_info(inode, lblk_num, &ci_offset, NULL); struct crypto_skcipher *tfm = ci->ci_enc_key->tfm; int res = 0; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 885a2ec3d711..2d08abbf5892 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -267,12 +267,13 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, { const struct fscrypt_info *ci; u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; + u64 ci_offset = 0; if (!fscrypt_inode_uses_inline_crypto(inode)) return; - ci = fscrypt_get_lblk_info(inode, first_lblk, NULL, NULL); + ci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL); - fscrypt_generate_dun(ci, first_lblk, dun); + fscrypt_generate_dun(ci, ci_offset, dun); bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask); } EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); @@ -350,13 +351,14 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, const struct bio_crypt_ctx *bc = bio->bi_crypt_context; u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; struct fscrypt_info *ci; + u64 ci_offset = 0; if (!!bc != fscrypt_inode_uses_inline_crypto(inode)) return false; if (!bc) return true; - ci = fscrypt_get_lblk_info(inode, next_lblk, NULL, NULL); + ci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL); /* * Comparing the key pointers is good enough, as all I/O for each key * uses the same pointer. I.e., there's currently no need to support @@ -365,7 +367,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, if (bc->bc_key != ci->ci_enc_key->blk_key) return false; - fscrypt_generate_dun(ci, next_lblk, next_dun); + fscrypt_generate_dun(ci, ci_offset, next_dun); return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun); } EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); @@ -460,6 +462,8 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) { const struct fscrypt_info *ci; u32 dun; + u64 ci_offset = 0; + u64 extent_len = 0; if (!fscrypt_inode_uses_inline_crypto(inode)) return nr_blocks; @@ -467,14 +471,18 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) if (nr_blocks <= 1) return nr_blocks; - ci = fscrypt_get_lblk_info(inode, lblk, NULL, NULL); + ci = fscrypt_get_lblk_info(inode, lblk, &ci_offset, &extent_len); + + /* Spanning an extent boundary will change the DUN */ + nr_blocks = min_t(u64, nr_blocks, extent_len); + if (!(fscrypt_policy_flags(&ci->ci_policy) & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) return nr_blocks; /* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */ - dun = ci->ci_hashed_ino + lblk; + dun = ci->ci_hashed_ino + ci_offset; return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun); } From patchwork Sat Sep 2 05:54:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373021 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05C40CA0FF6 for ; Sat, 2 Sep 2023 05:55:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351610AbjIBFzy (ORCPT ); Sat, 2 Sep 2023 01:55:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32878 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343615AbjIBFzx (ORCPT ); Sat, 2 Sep 2023 01:55:53 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0966210F4; Fri, 1 Sep 2023 22:55:50 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 68C88807EE; Sat, 2 Sep 2023 01:55:50 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634150; bh=b0hkPiFJJoeGPFIcEAEMWTWUyHVLSXX4xFrlWcwMaGU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HZxXhxmXRGRsxJDu7m5kcD2liS3Heh4gquDSrd5mBzDMrOJJbsQvtcDFU0NtRTb8i oIkPDu0lfiKYQ9FLr12s0V+cY6RVK2typusK6A1pAqtMmz/FJRVtrtd+ULH/NWN3lU sUbovEf6vMb5A6cgb7dLXcJf1CIsN/2dF5eTlXQ7/H8oLJYwCiJlJSvgrjppqc9ynl z72uAewr1PVBPL0kM/PexsENM6vBCmCU/4I1aXScU3oFue0galMV9UAfYGFJ/D7bOK FBXlJUL4A5CSxOZMV/1Ipd34hdLQ4SLnblpk01lNwpY6Mv/1tF0FmmVP0MQ7+M+BuP UjdAqm9sCc82g== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 03/13] fscrypt: move function call warning of busy inodes Date: Sat, 2 Sep 2023 01:54:21 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Extent encryption will want to attempt to evict inodes, and not warn of busy ones, before removing the key instead of after as it is at present. Therefore pull the call for check_for_busy_inodes() out of try_to_lock_encrypted_files() into its only callsite. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/keyring.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 7cbb1fd872ac..d153988b7403 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -938,8 +938,7 @@ static int check_for_busy_inodes(struct super_block *sb, static int try_to_lock_encrypted_files(struct super_block *sb, struct fscrypt_master_key *mk) { - int err1; - int err2; + int err; /* * An inode can't be evicted while it is dirty or has dirty pages. @@ -951,7 +950,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb, * already call sync_filesystem() via sys_syncfs() or sys_sync(). */ down_read(&sb->s_umount); - err1 = sync_filesystem(sb); + err = sync_filesystem(sb); up_read(&sb->s_umount); /* If a sync error occurs, still try to evict as much as possible. */ @@ -963,16 +962,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb, */ evict_dentries_for_decrypted_inodes(mk); - /* - * evict_dentries_for_decrypted_inodes() already iput() each inode in - * the list; any inodes for which that dropped the last reference will - * have been evicted due to fscrypt_drop_inode() detecting the key - * removal and telling the VFS to evict the inode. So to finish, we - * just need to check whether any inodes couldn't be evicted. - */ - err2 = check_for_busy_inodes(sb, mk); - - return err1 ?: err2; + return err; } /* @@ -1064,8 +1054,17 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) up_write(&mk->mk_sem); if (inodes_remain) { + int err2; /* Some inodes still reference this key; try to evict them. */ err = try_to_lock_encrypted_files(sb, mk); + /* We already tried to iput() each inode referencing this key + * which would cause the inode to be evicted if that was the + * last reference (since fscrypt_drop_inode() would see the + * key removal). So the only remaining inodes referencing this + * key are still busy and couldn't be evicted; check for them. + */ + err2 = check_for_busy_inodes(sb, mk); + err = err ?: err2; if (err == -EBUSY) { status_flags |= FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY; From patchwork Sat Sep 2 05:54:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 55825CA0FFD for ; Sat, 2 Sep 2023 05:56:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351616AbjIBF4C (ORCPT ); Sat, 2 Sep 2023 01:56:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32966 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343615AbjIBF4B (ORCPT ); Sat, 2 Sep 2023 01:56:01 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 83CFB10F4; Fri, 1 Sep 2023 22:55:55 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 46AE1803B3; Sat, 2 Sep 2023 01:55:53 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634155; bh=92wmtNloBcIIjyPTTS4nZf/dWbNcmusT5rrcU+kWjSc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=W9S8WHiXqVP17hCJiBaEttcXQ3MI6vHYHJYgWahGctkg6Ll9tzXacKy0L3OfIZ/81 hSJ1CPedwWkFtwLB8rd0EkFpICezQyKv9obuaPb4+rW+PJRjR/tmI1i9pNrm9fC/qk wGrmxn7TtwBK97v5Y7BAbzWj44NyuCoclvemWhhCc5YOrtiW3llwehuKK/QTCOpEG+ mmWo49A4/63Qg3jZ39vd6dTbjgArW/cahEH7a64Wj29VslvZXcERi8TvatsfR3QfED yeXvks48paySEZ7S1hgqfp5j7GVbhMJ7ZjxFGSeLIqxMOK2WjIvMuwQENfjFX6KrhS I9bqiBE+rqoKg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 04/13] fscrypt: split fscrypt_info into general and inode specific parts Date: Sat, 2 Sep 2023 01:54:22 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org In preparation for adding extent infos, split up the existing fscrypt_info structure into a fscrypt_common_info structure which tracks the information needed for data encryption and for freeing itself, and a fscrypt_info structure containing a fscrypt_common_info and the parts only needed for inodes. All external users continue to use a fscrypt_info, and most functions don't need to deal with inode-specific parts so they use fscrypt_common_infos. Similarly, split up the creation and freeing paths to have common parts that deal with fscrypt_common_infos, and inode-specific parts dealing in fscrypt_infos. Alternately, the common struct could be a fscrypt_info, and only the internal parts that need to deal in a specialized one could cast to the enclosing specialized type; however, this seems to be less typesafe. --- fs/crypto/crypto.c | 32 ++-- fs/crypto/fname.c | 13 +- fs/crypto/fscrypt_private.h | 122 +++++++++------ fs/crypto/hooks.c | 6 +- fs/crypto/inline_crypt.c | 62 ++++---- fs/crypto/keyring.c | 40 ++--- fs/crypto/keysetup.c | 296 +++++++++++++++++++++--------------- fs/crypto/keysetup_v1.c | 74 ++++----- fs/crypto/policy.c | 13 +- 9 files changed, 371 insertions(+), 287 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index d75f1b3f5795..d5c9326a1919 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * This contains encryption functions for per-file encryption. + * This contains encryption functions for fscrypt encryption. * * Copyright (C) 2015, Google, Inc. * Copyright (C) 2015, Motorola Mobility @@ -39,7 +39,7 @@ static mempool_t *fscrypt_bounce_page_pool = NULL; static struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); -struct kmem_cache *fscrypt_info_cachep; +struct kmem_cache *fscrypt_inode_info_cachep; void fscrypt_enqueue_decrypt_work(struct work_struct *work) { @@ -70,7 +70,7 @@ void fscrypt_free_bounce_page(struct page *bounce_page) EXPORT_SYMBOL(fscrypt_free_bounce_page); /* - * Generate the IV for the given logical block number within the given file. + * Generate the IV for the given logical block number within the given info. * For filenames encryption, lblk_num == 0. * * Keep this in sync with fscrypt_limit_io_blocks(). fscrypt_limit_io_blocks() @@ -78,21 +78,21 @@ EXPORT_SYMBOL(fscrypt_free_bounce_page); * simply contain the lblk_num (e.g., IV_INO_LBLK_32). */ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *cci) { - u8 flags = fscrypt_policy_flags(&ci->ci_policy); + u8 flags = fscrypt_policy_flags(&cci->ci_policy); - memset(iv, 0, ci->ci_mode->ivsize); + memset(iv, 0, cci->ci_mode->ivsize); if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { WARN_ON_ONCE(lblk_num > U32_MAX); - WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX); - lblk_num |= (u64)ci->ci_inode->i_ino << 32; + WARN_ON_ONCE(cci->ci_inode->i_ino > U32_MAX); + lblk_num |= (u64)cci->ci_inode->i_ino << 32; } else if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { WARN_ON_ONCE(lblk_num > U32_MAX); - lblk_num = (u32)(ci->ci_hashed_ino + lblk_num); + lblk_num = (u32)(cci->ci_hashed_ino + lblk_num); } else if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) { - memcpy(iv->nonce, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE); + memcpy(iv->nonce, cci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE); } iv->lblk_num = cpu_to_le64(lblk_num); } @@ -108,9 +108,9 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, DECLARE_CRYPTO_WAIT(wait); struct scatterlist dst, src; u64 ci_offset = 0; - struct fscrypt_info *ci = + struct fscrypt_common_info *cci = fscrypt_get_lblk_info(inode, lblk_num, &ci_offset, NULL); - struct crypto_skcipher *tfm = ci->ci_enc_key->tfm; + struct crypto_skcipher *tfm = cci->ci_enc_key->tfm; int res = 0; if (WARN_ON_ONCE(len <= 0)) @@ -118,7 +118,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0)) return -EINVAL; - fscrypt_generate_iv(&iv, lblk_num, ci); + fscrypt_generate_iv(&iv, lblk_num, cci); req = skcipher_request_alloc(tfm, gfp_flags); if (!req) @@ -393,8 +393,8 @@ static int __init fscrypt_init(void) if (!fscrypt_read_workqueue) goto fail; - fscrypt_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT); - if (!fscrypt_info_cachep) + fscrypt_inode_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT); + if (!fscrypt_inode_info_cachep) goto fail_free_queue; err = fscrypt_init_keyring(); @@ -404,7 +404,7 @@ static int __init fscrypt_init(void) return 0; fail_free_info: - kmem_cache_destroy(fscrypt_info_cachep); + kmem_cache_destroy(fscrypt_inode_info_cachep); fail_free_queue: destroy_workqueue(fscrypt_read_workqueue); fail: diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index edb78cd1b0e7..ae20a886dbdf 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -101,7 +101,8 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, struct skcipher_request *req = NULL; DECLARE_CRYPTO_WAIT(wait); const struct fscrypt_info *ci = inode->i_crypt_info; - struct crypto_skcipher *tfm = ci->ci_enc_key->tfm; + const struct fscrypt_common_info *cci = &ci->info; + struct crypto_skcipher *tfm = cci->ci_enc_key->tfm; union fscrypt_iv iv; struct scatterlist sg; int res; @@ -116,7 +117,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, memset(out + iname->len, 0, olen - iname->len); /* Initialize the IV */ - fscrypt_generate_iv(&iv, 0, ci); + fscrypt_generate_iv(&iv, 0, cci); /* Set up the encryption request */ req = skcipher_request_alloc(tfm, GFP_NOFS); @@ -158,7 +159,8 @@ static int fname_decrypt(const struct inode *inode, DECLARE_CRYPTO_WAIT(wait); struct scatterlist src_sg, dst_sg; const struct fscrypt_info *ci = inode->i_crypt_info; - struct crypto_skcipher *tfm = ci->ci_enc_key->tfm; + const struct fscrypt_common_info *cci = &ci->info; + struct crypto_skcipher *tfm = cci->ci_enc_key->tfm; union fscrypt_iv iv; int res; @@ -171,7 +173,7 @@ static int fname_decrypt(const struct inode *inode, crypto_req_done, &wait); /* Initialize IV */ - fscrypt_generate_iv(&iv, 0, ci); + fscrypt_generate_iv(&iv, 0, cci); /* Create decryption request */ sg_init_one(&src_sg, iname->name, iname->len); @@ -299,7 +301,8 @@ bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret) { - return __fscrypt_fname_encrypted_size(&inode->i_crypt_info->ci_policy, + struct fscrypt_common_info *cci = &inode->i_crypt_info->info; + return __fscrypt_fname_encrypted_size(&cci->ci_policy, orig_len, max_len, encrypted_len_ret); } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8a1fd1d33cfc..cc1a61ade2a4 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -53,14 +53,15 @@ struct fscrypt_context_v2 { }; /* - * fscrypt_context - the encryption context of an inode + * fscrypt_context - the encryption context of an object * * This is the on-disk equivalent of an fscrypt_policy, stored alongside each - * encrypted file usually in a hidden extended attribute. It contains the - * fields from the fscrypt_policy, in order to identify the encryption algorithm - * and key with which the file is encrypted. It also contains a nonce that was - * randomly generated by fscrypt itself; this is used as KDF input or as a tweak - * to cause different files to be encrypted differently. + * encrypted object, usually in a hidden extended attribute for a file. It + * contains the fields from the fscrypt_policy, in order to identify the + * encryption algorithm and key with which the file is encrypted. It also + * contains a nonce that was randomly generated by fscrypt itself; this is used + * as KDF input or as a tweak to cause different objects to be encrypted + * differently. */ union fscrypt_context { u8 version; @@ -209,32 +210,41 @@ struct fscrypt_prepared_key { enum fscrypt_prepared_key_type type; }; +typedef enum { + CI_INODE = 1, + CI_EXTENT, +} __packed fscrypt_ci_type_t; + /* - * fscrypt_info - the "encryption key" for an inode + * fscrypt_common_info -- shared objects needed for data encryption * - * When an encrypted file's key is made available, an instance of this struct is - * allocated and stored in ->i_crypt_info. Once created, it remains until the - * inode is evicted. + * This keeps track of the information needed to actually encrypt/decrypt data + * (the prepared key, nonce, inode, policy, superblock) and the master key + * information needed to free this info. */ -struct fscrypt_info { +struct fscrypt_common_info { /* The key in a form prepared for actual encryption/decryption */ struct fscrypt_prepared_key *ci_enc_key; - /* True if ci_enc_key should be freed when this fscrypt_info is freed */ + /* + * True if ci_enc_key should be freed when this fscrypt_common_info is + * freed. + */ bool ci_owns_key; #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT /* - * True if this inode will use inline encryption (blk-crypto) instead of + * True if this info will use inline encryption (blk-crypto) instead of * the traditional filesystem-layer encryption. */ bool ci_inlinecrypt; #endif + fscrypt_ci_type_t ci_type; /* - * Encryption mode used for this inode. It corresponds to either the - * contents or filenames encryption mode, depending on the inode type. + * Encryption mode used for this info. It corresponds to either the + * contents or filenames encryption mode, depending on the info. */ struct fscrypt_mode *ci_mode; @@ -242,18 +252,38 @@ struct fscrypt_info { struct inode *ci_inode; /* - * The master key with which this inode was unlocked (decrypted). This + * The master key with which this info was unlocked (decrypted). This * will be NULL if the master key was found in a process-subscribed * keyring rather than in the filesystem-level keyring. */ struct fscrypt_master_key *ci_master_key; /* - * Link in list of inodes that were unlocked with the master key. + * Link in list of infos that were unlocked with the master key. * Only used when ->ci_master_key is set. */ struct list_head ci_master_key_link; + /* The encryption policy used by this object */ + union fscrypt_policy ci_policy; + + /* This object'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; +}; + +/* + * fscrypt_info - the "encryption key" for an inode + * + * When an encrypted file's key is made available, an instance of this struct is + * allocated and stored in ->i_crypt_info. Once created, it remains until the + * inode is evicted. + */ +struct fscrypt_info { + struct fscrypt_common_info info; + /* * This inode's hash key for filenames. This is a 128-bit SipHash-2-4 * key. This is only set for directories that use a keyed dirhash over @@ -261,24 +291,19 @@ struct fscrypt_info { */ 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 { FS_DECRYPT = 0, FS_ENCRYPT, } fscrypt_direction_t; /** - * fscrypt_get_lblk_info() - get the fscrypt_info to crypt a particular block + * fscrypt_get_lblk_info() - get the fscrypt_common_info to crypt a particular + * block * * @inode: the inode to which the block belongs * @lblk: the offset of the block within the file which the inode @@ -293,7 +318,7 @@ typedef enum { * * Return: the appropriate fscrypt_info if there is one, else NULL. */ -static inline struct fscrypt_info * +static inline struct fscrypt_common_info * fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset, u64 *extent_len) { @@ -302,12 +327,12 @@ fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset, if (extent_len) *extent_len = U64_MAX; - return inode->i_crypt_info; + return &inode->i_crypt_info->info; } /* crypto.c */ -extern struct kmem_cache *fscrypt_info_cachep; +extern struct kmem_cache *fscrypt_inode_info_cachep; int fscrypt_initialize(struct super_block *sb); int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, u64 lblk_num, struct page *src_page, @@ -338,7 +363,7 @@ union fscrypt_iv { }; void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, - const struct fscrypt_info *ci); + const struct fscrypt_common_info *ci); /* fname.c */ bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, @@ -376,17 +401,17 @@ void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); /* inline_crypt.c */ #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT -int fscrypt_select_encryption_impl(struct fscrypt_info *ci); +int fscrypt_select_encryption_impl(struct fscrypt_common_info *ci); static inline bool -fscrypt_using_inline_encryption(const struct fscrypt_info *ci) +fscrypt_using_inline_encryption(const struct fscrypt_common_info *ci) { return ci->ci_inlinecrypt; } int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, - const struct fscrypt_info *ci); + const struct fscrypt_common_info *ci); void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key); @@ -397,7 +422,7 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, */ static inline bool fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *ci) { /* * The two smp_load_acquire()'s here pair with the smp_store_release()'s @@ -414,13 +439,13 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ -static inline int fscrypt_select_encryption_impl(struct fscrypt_info *ci) +static inline int fscrypt_select_encryption_impl(struct fscrypt_common_info *ci) { return 0; } static inline bool -fscrypt_using_inline_encryption(const struct fscrypt_info *ci) +fscrypt_using_inline_encryption(const struct fscrypt_common_info *ci) { return false; } @@ -428,7 +453,7 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci) static inline int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *ci) { WARN_ON_ONCE(1); return -EOPNOTSUPP; @@ -442,7 +467,7 @@ fscrypt_destroy_inline_crypt_key(struct super_block *sb, static inline bool fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *ci) { return smp_load_acquire(&prep_key->tfm) != NULL; } @@ -499,7 +524,7 @@ struct fscrypt_master_key { * A structural ref only guarantees that the struct continues to exist. * * There is one active ref associated with ->mk_secret being present, - * and one active ref for each inode in ->mk_decrypted_inodes. + * and one active ref for each inode in ->mk_decrypted_infos. * * There is one structural ref associated with the active refcount being * nonzero. Finding a key in the keyring also takes a structural ref, @@ -513,7 +538,7 @@ struct fscrypt_master_key { /* * The secret key material. After FS_IOC_REMOVE_ENCRYPTION_KEY is * executed, this is wiped and no new inodes can be unlocked with this - * key; however, there may still be inodes in ->mk_decrypted_inodes + * key; however, there may still be inodes in ->mk_decrypted_infos * which could not be evicted. As long as some inodes still remain, * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again. @@ -552,8 +577,8 @@ struct fscrypt_master_key { * List of inodes that were unlocked using this key. This allows the * inodes to be evicted efficiently if the key is removed. */ - struct list_head mk_decrypted_inodes; - spinlock_t mk_decrypted_inodes_lock; + struct list_head mk_decrypted_infos; + spinlock_t mk_decrypted_infos_lock; /* * Per-mode encryption keys for the various types of encryption policies @@ -642,17 +667,18 @@ struct fscrypt_mode { extern struct fscrypt_mode fscrypt_modes[]; int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, - const u8 *raw_key, const struct fscrypt_info *ci); + const u8 *raw_key, + const struct fscrypt_common_info *ci); void fscrypt_destroy_prepared_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key); -int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key); +int fscrypt_set_per_info_enc_key(struct fscrypt_common_info *ci, const u8 *raw_key); int fscrypt_derive_dirhash_key(struct fscrypt_info *ci, const struct fscrypt_master_key *mk); -void fscrypt_hash_inode_number(struct fscrypt_info *ci, +void fscrypt_hash_inode_number(struct fscrypt_common_info *ci, const struct fscrypt_master_key *mk); int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported); @@ -687,10 +713,10 @@ static inline int fscrypt_require_key(struct inode *inode) void fscrypt_put_direct_key(struct fscrypt_prepared_key *prep_key); -int fscrypt_setup_v1_file_key(struct fscrypt_info *ci, +int fscrypt_setup_v1_info_key(struct fscrypt_common_info *ci, const u8 *raw_master_key); -int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci); +int fscrypt_setup_v1_info_key_via_subscribed_keyrings(struct fscrypt_common_info *ci); /* policy.c */ diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 6238dbcadcad..3c1d51724768 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -170,6 +170,7 @@ int fscrypt_prepare_setflags(struct inode *inode, unsigned int oldflags, unsigned int flags) { struct fscrypt_info *ci; + struct fscrypt_common_info *cci; struct fscrypt_master_key *mk; int err; @@ -183,9 +184,10 @@ int fscrypt_prepare_setflags(struct inode *inode, if (err) return err; ci = inode->i_crypt_info; - if (ci->ci_policy.version != FSCRYPT_POLICY_V2) + cci = &ci->info; + if (cci->ci_policy.version != FSCRYPT_POLICY_V2) return -EINVAL; - mk = ci->ci_master_key; + mk = cci->ci_master_key; down_read(&mk->mk_sem); if (is_master_key_secret_present(&mk->mk_secret)) err = fscrypt_derive_dirhash_key(ci, mk); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 2d08abbf5892..09ec82b8a98a 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -39,10 +39,10 @@ static struct block_device **fscrypt_get_devices(struct super_block *sb, return devs; } -static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci) +static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_common_info *cci) { - struct super_block *sb = ci->ci_inode->i_sb; - unsigned int flags = fscrypt_policy_flags(&ci->ci_policy); + struct super_block *sb = cci->ci_inode->i_sb; + unsigned int flags = fscrypt_policy_flags(&cci->ci_policy); int ino_bits = 64, lblk_bits = 64; if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) @@ -90,9 +90,9 @@ static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode, } /* Enable inline encryption for this file if supported. */ -int fscrypt_select_encryption_impl(struct fscrypt_info *ci) +int fscrypt_select_encryption_impl(struct fscrypt_common_info *cci) { - const struct inode *inode = ci->ci_inode; + const struct inode *inode = cci->ci_inode; struct super_block *sb = inode->i_sb; struct blk_crypto_config crypto_cfg; struct block_device **devs; @@ -104,7 +104,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) return 0; /* The crypto mode must have a blk-crypto counterpart */ - if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID) + if (cci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID) return 0; /* The filesystem must be mounted with -o inlinecrypt */ @@ -119,7 +119,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) * doesn't work with IV_INO_LBLK_32. For now, simply exclude * IV_INO_LBLK_32 with blocksize != PAGE_SIZE from inline encryption. */ - if ((fscrypt_policy_flags(&ci->ci_policy) & + if ((fscrypt_policy_flags(&cci->ci_policy) & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) && sb->s_blocksize != PAGE_SIZE) return 0; @@ -128,9 +128,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) * On all the filesystem's block devices, blk-crypto must support the * crypto configuration that the file would use. */ - crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; + crypto_cfg.crypto_mode = cci->ci_mode->blk_crypto_mode; crypto_cfg.data_unit_size = sb->s_blocksize; - crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); + crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(cci); devs = fscrypt_get_devices(sb, &num_devs); if (IS_ERR(devs)) @@ -141,9 +141,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) goto out_free_devs; } - fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg); + fscrypt_log_blk_crypto_impl(cci->ci_mode, devs, num_devs, &crypto_cfg); - ci->ci_inlinecrypt = true; + cci->ci_inlinecrypt = true; out_free_devs: kfree(devs); @@ -152,11 +152,11 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *cci) { - const struct inode *inode = ci->ci_inode; + const struct inode *inode = cci->ci_inode; struct super_block *sb = inode->i_sb; - enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; + enum blk_crypto_mode_num crypto_mode = cci->ci_mode->blk_crypto_mode; struct blk_crypto_key *blk_key; struct block_device **devs; unsigned int num_devs; @@ -168,7 +168,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, return -ENOMEM; err = blk_crypto_init_key(blk_key, raw_key, crypto_mode, - fscrypt_get_dun_bytes(ci), sb->s_blocksize); + fscrypt_get_dun_bytes(cci), sb->s_blocksize); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); goto fail; @@ -228,21 +228,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) { - return inode->i_crypt_info->ci_inlinecrypt; + return inode->i_crypt_info->info.ci_inlinecrypt; } EXPORT_SYMBOL_GPL(__fscrypt_inode_uses_inline_crypto); -static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num, +static void fscrypt_generate_dun(const struct fscrypt_common_info *cci, u64 lblk_num, u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]) { union fscrypt_iv iv; int i; - fscrypt_generate_iv(&iv, lblk_num, ci); + fscrypt_generate_iv(&iv, lblk_num, cci); BUILD_BUG_ON(FSCRYPT_MAX_IV_SIZE > BLK_CRYPTO_MAX_IV_SIZE); memset(dun, 0, BLK_CRYPTO_MAX_IV_SIZE); - for (i = 0; i < ci->ci_mode->ivsize/sizeof(dun[0]); i++) + for (i = 0; i < cci->ci_mode->ivsize/sizeof(dun[0]); i++) dun[i] = le64_to_cpu(iv.dun[i]); } @@ -265,16 +265,16 @@ static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num, void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, u64 first_lblk, gfp_t gfp_mask) { - const struct fscrypt_info *ci; + const struct fscrypt_common_info *cci; u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; u64 ci_offset = 0; if (!fscrypt_inode_uses_inline_crypto(inode)) return; - ci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL); + cci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL); - fscrypt_generate_dun(ci, ci_offset, dun); - bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask); + fscrypt_generate_dun(cci, ci_offset, dun); + bio_crypt_set_ctx(bio, cci->ci_enc_key->blk_key, dun, gfp_mask); } EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); @@ -350,7 +350,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, { const struct bio_crypt_ctx *bc = bio->bi_crypt_context; u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; - struct fscrypt_info *ci; + struct fscrypt_common_info *cci; u64 ci_offset = 0; if (!!bc != fscrypt_inode_uses_inline_crypto(inode)) @@ -358,16 +358,16 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, if (!bc) return true; - ci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL); + cci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL); /* * Comparing the key pointers is good enough, as all I/O for each key * uses the same pointer. I.e., there's currently no need to support * merging requests where the keys are the same but the pointers differ. */ - if (bc->bc_key != ci->ci_enc_key->blk_key) + if (bc->bc_key != cci->ci_enc_key->blk_key) return false; - fscrypt_generate_dun(ci, ci_offset, next_dun); + fscrypt_generate_dun(cci, ci_offset, next_dun); return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun); } EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); @@ -460,7 +460,7 @@ EXPORT_SYMBOL_GPL(fscrypt_dio_supported); */ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) { - const struct fscrypt_info *ci; + const struct fscrypt_common_info *cci; u32 dun; u64 ci_offset = 0; u64 extent_len = 0; @@ -471,18 +471,18 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) if (nr_blocks <= 1) return nr_blocks; - ci = fscrypt_get_lblk_info(inode, lblk, &ci_offset, &extent_len); + cci = fscrypt_get_lblk_info(inode, lblk, &ci_offset, &extent_len); /* Spanning an extent boundary will change the DUN */ nr_blocks = min_t(u64, nr_blocks, extent_len); - if (!(fscrypt_policy_flags(&ci->ci_policy) & + if (!(fscrypt_policy_flags(&cci->ci_policy) & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) return nr_blocks; /* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */ - dun = ci->ci_hashed_ino + ci_offset; + dun = cci->ci_hashed_ino + ci_offset; return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun); } diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index d153988b7403..27ae0345fa85 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -100,10 +100,10 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, /* * ->mk_active_refs == 0 implies that ->mk_secret is not present and - * that ->mk_decrypted_inodes is empty. + * that ->mk_decrypted_infos is empty. */ WARN_ON_ONCE(is_master_key_secret_present(&mk->mk_secret)); - WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); + WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_infos)); for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { fscrypt_destroy_prepared_key( @@ -426,8 +426,8 @@ static int add_new_master_key(struct super_block *sb, refcount_set(&mk->mk_struct_refs, 1); mk->mk_spec = *mk_spec; - INIT_LIST_HEAD(&mk->mk_decrypted_inodes); - spin_lock_init(&mk->mk_decrypted_inodes_lock); + INIT_LIST_HEAD(&mk->mk_decrypted_infos); + spin_lock_init(&mk->mk_decrypted_infos_lock); if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { err = allocate_master_key_users_keyring(mk); @@ -865,16 +865,16 @@ static void shrink_dcache_inode(struct inode *inode) d_prune_aliases(inode); } -static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk) +static void evict_dentries_for_decrypted_infos(struct fscrypt_master_key *mk) { - struct fscrypt_info *ci; + struct fscrypt_common_info *cci; struct inode *inode; struct inode *toput_inode = NULL; - spin_lock(&mk->mk_decrypted_inodes_lock); + spin_lock(&mk->mk_decrypted_infos_lock); - list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) { - inode = ci->ci_inode; + list_for_each_entry(cci, &mk->mk_decrypted_infos, ci_master_key_link) { + inode = cci->ci_inode; spin_lock(&inode->i_lock); if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) { spin_unlock(&inode->i_lock); @@ -882,16 +882,16 @@ static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk) } __iget(inode); spin_unlock(&inode->i_lock); - spin_unlock(&mk->mk_decrypted_inodes_lock); + spin_unlock(&mk->mk_decrypted_infos_lock); shrink_dcache_inode(inode); iput(toput_inode); toput_inode = inode; - spin_lock(&mk->mk_decrypted_inodes_lock); + spin_lock(&mk->mk_decrypted_infos_lock); } - spin_unlock(&mk->mk_decrypted_inodes_lock); + spin_unlock(&mk->mk_decrypted_infos_lock); iput(toput_inode); } @@ -903,25 +903,25 @@ static int check_for_busy_inodes(struct super_block *sb, unsigned long ino; char ino_str[50] = ""; - spin_lock(&mk->mk_decrypted_inodes_lock); + spin_lock(&mk->mk_decrypted_infos_lock); - list_for_each(pos, &mk->mk_decrypted_inodes) + list_for_each(pos, &mk->mk_decrypted_infos) busy_count++; if (busy_count == 0) { - spin_unlock(&mk->mk_decrypted_inodes_lock); + spin_unlock(&mk->mk_decrypted_infos_lock); return 0; } { /* select an example file to show for debugging purposes */ struct inode *inode = - list_first_entry(&mk->mk_decrypted_inodes, - struct fscrypt_info, + list_first_entry(&mk->mk_decrypted_infos, + struct fscrypt_common_info, ci_master_key_link)->ci_inode; ino = inode->i_ino; } - spin_unlock(&mk->mk_decrypted_inodes_lock); + spin_unlock(&mk->mk_decrypted_infos_lock); /* If the inode is currently being created, ino may still be 0. */ if (ino) @@ -942,7 +942,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb, /* * An inode can't be evicted while it is dirty or has dirty pages. - * Thus, we first have to clean the inodes in ->mk_decrypted_inodes. + * Thus, we first have to clean the inodes in ->mk_decrypted_infos. * * Just do it the easy way: call sync_filesystem(). It's overkill, but * it works, and it's more important to minimize the amount of caches we @@ -960,7 +960,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb, * and inappropriate for use by unprivileged users. So instead go * through the inodes' alias lists and try to evict each dentry. */ - evict_dentries_for_decrypted_inodes(mk); + evict_dentries_for_decrypted_infos(mk); return err; } diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index a19650f954e2..ae250e432dcd 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -101,7 +101,7 @@ select_encryption_mode(const union fscrypt_policy *policy, if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) return &fscrypt_modes[fscrypt_policy_fnames_mode(policy)]; - WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n", + WARN_ONCE(1, "fscrypt: filesystem tried to load an encryption info for inode %lu, which is not encryptable (file type %d)\n", inode->i_ino, (inode->i_mode & S_IFMT)); return ERR_PTR(-EINVAL); } @@ -159,14 +159,14 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key, * and IV generation method (@ci->ci_policy.flags). */ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, - const u8 *raw_key, const struct fscrypt_info *ci) + const u8 *raw_key, const struct fscrypt_common_info *cci) { struct crypto_skcipher *tfm; - if (fscrypt_using_inline_encryption(ci)) - return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci); + if (fscrypt_using_inline_encryption(cci)) + return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, cci); - tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); + tfm = fscrypt_allocate_skcipher(cci->ci_mode, raw_key, cci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); /* @@ -188,15 +188,16 @@ void fscrypt_destroy_prepared_key(struct super_block *sb, memzero_explicit(prep_key, sizeof(*prep_key)); } -/* Given a per-file encryption key, set up the file's crypto transform object */ -int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key) +/* Given a per-info encryption key, set up the info's crypto transform object */ +int fscrypt_set_per_info_enc_key(struct fscrypt_common_info *cci, + const u8 *raw_key) { - ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL); - if (!ci->ci_enc_key) + cci->ci_enc_key = kzalloc(sizeof(*cci->ci_enc_key), GFP_KERNEL); + if (!cci->ci_enc_key) return -ENOMEM; - ci->ci_enc_key->type = FSCRYPT_KEY_PER_INFO; - return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci); + cci->ci_enc_key->type = FSCRYPT_KEY_PER_INFO; + return fscrypt_prepare_key(cci->ci_enc_key, raw_key, cci); } static struct fscrypt_prepared_key * @@ -219,15 +220,15 @@ mk_prepared_key_for_mode_policy(struct fscrypt_master_key *mk, } static size_t -fill_hkdf_info_for_mode_key(const struct fscrypt_info *ci, +fill_hkdf_info_for_mode_key(const struct fscrypt_common_info *cci, u8 hkdf_info[MAX_MODE_KEY_HKDF_INFO_SIZE]) { - const u8 mode_num = ci->ci_mode - fscrypt_modes; - const struct super_block *sb = ci->ci_inode->i_sb; + const u8 mode_num = cci->ci_mode - fscrypt_modes; + const struct super_block *sb = cci->ci_inode->i_sb; u8 hkdf_infolen = 0; hkdf_info[hkdf_infolen++] = mode_num; - if (!(ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) { + if (!(cci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) { memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, sizeof(sb->s_uuid)); hkdf_infolen += sizeof(sb->s_uuid); @@ -237,12 +238,12 @@ fill_hkdf_info_for_mode_key(const struct fscrypt_info *ci, static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk, struct fscrypt_prepared_key *prep_key, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *cci) { - const struct inode *inode = ci->ci_inode; + const struct inode *inode = cci->ci_inode; const struct super_block *sb = inode->i_sb; - unsigned int policy_flags = fscrypt_policy_flags(&ci->ci_policy); - struct fscrypt_mode *mode = ci->ci_mode; + unsigned int policy_flags = fscrypt_policy_flags(&cci->ci_policy); + struct fscrypt_mode *mode = cci->ci_mode; const u8 mode_num = mode - fscrypt_modes; u8 mode_key[FSCRYPT_MAX_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; @@ -263,8 +264,8 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk, } /* - * For DIRECT_KEY policies: instead of deriving per-file encryption - * keys, the per-file nonce will be included in all the IVs. But + * For DIRECT_KEY policies: instead of deriving per-info encryption + * keys, the per-info nonce will be included in all the IVs. But * unlike v1 policies, for v2 policies in this case we don't encrypt * with the master key directly but rather derive a per-mode encryption * key. This ensures that the master key is consistently used only for @@ -278,13 +279,13 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk, mutex_lock(&fscrypt_mode_key_setup_mutex); - if (fscrypt_is_key_prepared(prep_key, ci)) + if (fscrypt_is_key_prepared(prep_key, cci)) goto out_unlock; BUILD_BUG_ON(sizeof(mode_num) != 1); BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); BUILD_BUG_ON(sizeof(hkdf_info) != MAX_MODE_KEY_HKDF_INFO_SIZE); - hkdf_infolen = fill_hkdf_info_for_mode_key(ci, hkdf_info); + hkdf_infolen = fill_hkdf_info_for_mode_key(cci, hkdf_info); err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info, hkdf_infolen, @@ -292,7 +293,7 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk, if (err) return err; prep_key->type = FSCRYPT_KEY_MASTER_KEY; - err = fscrypt_prepare_key(prep_key, mode_key, ci); + err = fscrypt_prepare_key(prep_key, mode_key, cci); memzero_explicit(mode_key, mode->keysize); out_unlock: @@ -300,10 +301,10 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk, return err; } -static int setup_mode_prepared_key(struct fscrypt_info *ci, +static int setup_mode_prepared_key(struct fscrypt_common_info *cci, struct fscrypt_master_key *mk) { - struct fscrypt_mode *mode = ci->ci_mode; + struct fscrypt_mode *mode = cci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; int err; @@ -311,19 +312,19 @@ static int setup_mode_prepared_key(struct fscrypt_info *ci, if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX)) return -EINVAL; - prep_key = mk_prepared_key_for_mode_policy(mk, &ci->ci_policy, mode); + prep_key = mk_prepared_key_for_mode_policy(mk, &cci->ci_policy, mode); if (IS_ERR(prep_key)) return PTR_ERR(prep_key); - if (fscrypt_is_key_prepared(prep_key, ci)) { - ci->ci_enc_key = prep_key; + if (fscrypt_is_key_prepared(prep_key, cci)) { + cci->ci_enc_key = prep_key; return 0; } - err = setup_new_mode_prepared_key(mk, prep_key, ci); + err = setup_new_mode_prepared_key(mk, prep_key, cci); if (err) return err; - ci->ci_enc_key = prep_key; + cci->ci_enc_key = prep_key; return 0; } @@ -359,7 +360,7 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci, int err; err = fscrypt_derive_siphash_key(mk, HKDF_CONTEXT_DIRHASH_KEY, - ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE, + ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE, &ci->ci_dirhash_key); if (err) return err; @@ -367,14 +368,14 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci, return 0; } -void fscrypt_hash_inode_number(struct fscrypt_info *ci, +void fscrypt_hash_inode_number(struct fscrypt_common_info *cci, const struct fscrypt_master_key *mk) { - WARN_ON_ONCE(ci->ci_inode->i_ino == 0); + WARN_ON_ONCE(cci->ci_inode->i_ino == 0); WARN_ON_ONCE(!mk->mk_ino_hash_key_initialized); - ci->ci_hashed_ino = (u32)siphash_1u64(ci->ci_inode->i_ino, - &mk->mk_ino_hash_key); + cci->ci_hashed_ino = (u32)siphash_1u64(cci->ci_inode->i_ino, + &mk->mk_ino_hash_key); } static int fscrypt_setup_ino_hash_key(struct fscrypt_master_key *mk) @@ -403,25 +404,25 @@ static int fscrypt_setup_ino_hash_key(struct fscrypt_master_key *mk) return err; } -static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, +static int fscrypt_setup_v2_info_key(struct fscrypt_common_info *cci, struct fscrypt_master_key *mk) { int err; - if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAGS_KEY_MASK) { - err = setup_mode_prepared_key(ci, mk); + if (cci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAGS_KEY_MASK) { + err = setup_mode_prepared_key(cci, mk); } else { u8 derived_key[FSCRYPT_MAX_KEY_SIZE]; err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_PER_FILE_ENC_KEY, - ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE, - derived_key, ci->ci_mode->keysize); + cci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE, + derived_key, cci->ci_mode->keysize); if (err) return err; - err = fscrypt_set_per_file_enc_key(ci, derived_key); - memzero_explicit(derived_key, ci->ci_mode->keysize); + err = fscrypt_set_per_info_enc_key(cci, derived_key); + memzero_explicit(derived_key, cci->ci_mode->keysize); } return err; @@ -430,13 +431,13 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, /* * Find or create the appropriate prepared key for an info. */ -static int fscrypt_setup_file_key(struct fscrypt_info *ci, +static int fscrypt_setup_info_key(struct fscrypt_common_info *cci, struct fscrypt_master_key *mk) { int err; if (!mk) { - if (ci->ci_policy.version != FSCRYPT_POLICY_V1) + if (cci->ci_policy.version != FSCRYPT_POLICY_V1) return -ENOKEY; /* @@ -445,15 +446,15 @@ static int fscrypt_setup_file_key(struct fscrypt_info *ci, * to before the search of ->s_master_keys, since users * shouldn't be able to override filesystem-level keys. */ - return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci); + return fscrypt_setup_v1_info_key_via_subscribed_keyrings(cci); } - switch (ci->ci_policy.version) { + switch (cci->ci_policy.version) { case FSCRYPT_POLICY_V1: - err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw); + err = fscrypt_setup_v1_info_key(cci, mk->mk_secret.raw); break; case FSCRYPT_POLICY_V2: - err = fscrypt_setup_v2_file_key(ci, mk); + err = fscrypt_setup_v2_info_key(cci, mk); break; default: WARN_ON_ONCE(1); @@ -465,30 +466,30 @@ static int fscrypt_setup_file_key(struct fscrypt_info *ci, /* * Check whether the size of the given master key (@mk) is appropriate for the - * encryption settings which a particular file will use (@ci). + * encryption settings which a particular info will use (@cci). * - * If the file uses a v1 encryption policy, then the master key must be at least + * If the info uses a v1 encryption policy, then the master key must be at least * as long as the derived key, as this is a requirement of the v1 KDF. * * Otherwise, the KDF can accept any size key, so we enforce a slightly looser * requirement: we require that the size of the master key be at least the * maximum security strength of any algorithm whose key will be derived from it - * (but in practice we only need to consider @ci->ci_mode, since any other + * (but in practice we only need to consider @cci->ci_mode, since any other * possible subkeys such as DIRHASH and INODE_HASH will never increase the - * required key size over @ci->ci_mode). This allows AES-256-XTS keys to be + * required key size over @cci->ci_mode). This allows AES-256-XTS keys to be * derived from a 256-bit master key, which is cryptographically sufficient, * rather than requiring a 512-bit master key which is unnecessarily long. (We * still allow 512-bit master keys if the user chooses to use them, though.) */ static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk, - const struct fscrypt_info *ci) + const struct fscrypt_common_info *cci) { unsigned int min_keysize; - if (ci->ci_policy.version == FSCRYPT_POLICY_V1) - min_keysize = ci->ci_mode->keysize; + if (cci->ci_policy.version == FSCRYPT_POLICY_V1) + min_keysize = cci->ci_mode->keysize; else - min_keysize = ci->ci_mode->security_strength; + min_keysize = cci->ci_mode->security_strength; if (mk->mk_secret.size < min_keysize) { fscrypt_warn(NULL, @@ -507,19 +508,19 @@ static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk, * * If the master key is found in the filesystem-level keyring, then it is * returned in *mk_ret with its semaphore read-locked. This is needed to ensure - * that only one task links the fscrypt_info into ->mk_decrypted_inodes (as + * that only one task links inode fscrypt_info into ->mk_decrypted_infos (as * multiple tasks may race to create an fscrypt_info for the same inode), and to * synchronize the master key being removed with a new inode starting to use it. */ -static int find_and_lock_master_key(const struct fscrypt_info *ci, +static int find_and_lock_master_key(const struct fscrypt_common_info *cci, struct fscrypt_master_key **mk_ret) { - struct super_block *sb = ci->ci_inode->i_sb; + struct super_block *sb = cci->ci_inode->i_sb; struct fscrypt_key_specifier mk_spec; struct fscrypt_master_key *mk; int err; - err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec); + err = fscrypt_policy_to_key_spec(&cci->ci_policy, &mk_spec); if (err) return err; @@ -529,13 +530,13 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci, fscrypt_get_dummy_policy(sb); /* - * Add the test_dummy_encryption key on-demand. In principle, + * Add the test_dummy_encryption key on-demand. In princciple, * it should be added at mount time. Do it here instead so that * the individual filesystems don't need to worry about adding * this key at mount time and cleaning up on mount failure. */ if (dummy_policy && - fscrypt_policies_equal(dummy_policy, &ci->ci_policy)) { + fscrypt_policies_equal(dummy_policy, &cci->ci_policy)) { err = fscrypt_add_test_dummy_key(sb, &mk_spec); if (err) return err; @@ -544,7 +545,7 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci, } if (unlikely(!mk)) { - if (ci->ci_policy.version != FSCRYPT_POLICY_V1) + if (cci->ci_policy.version != FSCRYPT_POLICY_V1) return -ENOKEY; /* @@ -564,7 +565,7 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci, goto out_release_key; } - if (!fscrypt_valid_master_key_size(mk, ci)) { + if (!fscrypt_valid_master_key_size(mk, cci)) { err = -ENOKEY; goto out_release_key; } @@ -578,26 +579,24 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci, return err; } -static void put_crypt_info(struct fscrypt_info *ci) +static void free_prepared_key(struct fscrypt_common_info *cci) { - struct fscrypt_master_key *mk; - - if (!ci) - return; - - if (ci->ci_enc_key) { - enum fscrypt_prepared_key_type type = ci->ci_enc_key->type; + if (cci->ci_enc_key) { + enum fscrypt_prepared_key_type type = cci->ci_enc_key->type; if (type == FSCRYPT_KEY_DIRECT_V1) - fscrypt_put_direct_key(ci->ci_enc_key); + fscrypt_put_direct_key(cci->ci_enc_key); if (type == FSCRYPT_KEY_PER_INFO) { - fscrypt_destroy_prepared_key(ci->ci_inode->i_sb, - ci->ci_enc_key); - kfree_sensitive(ci->ci_enc_key); + fscrypt_destroy_prepared_key(cci->ci_inode->i_sb, + cci->ci_enc_key); + kfree_sensitive(cci->ci_enc_key); } } +} - mk = ci->ci_master_key; +static void remove_info_from_mk_decrypted_list(struct fscrypt_common_info *cci) +{ + struct fscrypt_master_key *mk = cci->ci_master_key; if (mk) { /* * Remove this inode from the list of inodes that were unlocked @@ -605,22 +604,36 @@ static void put_crypt_info(struct fscrypt_info *ci) * inode from a master key struct that already had its secret * removed, then complete the full removal of the struct. */ - spin_lock(&mk->mk_decrypted_inodes_lock); - list_del(&ci->ci_master_key_link); - spin_unlock(&mk->mk_decrypted_inodes_lock); - fscrypt_put_master_key_activeref(ci->ci_inode->i_sb, mk); + spin_lock(&mk->mk_decrypted_infos_lock); + list_del(&cci->ci_master_key_link); + spin_unlock(&mk->mk_decrypted_infos_lock); + fscrypt_put_master_key_activeref(cci->ci_inode->i_sb, mk); } +} + +static void put_crypt_inode_info(struct fscrypt_info *ci) +{ + if (!ci) + return; + + free_prepared_key(&ci->info); + remove_info_from_mk_decrypted_list(&ci->info); + memzero_explicit(ci, sizeof(*ci)); - kmem_cache_free(fscrypt_info_cachep, ci); + kmem_cache_free(fscrypt_inode_info_cachep, ci); } -static int -fscrypt_setup_encryption_info(struct inode *inode, - const union fscrypt_policy *policy, - const u8 nonce[FSCRYPT_FILE_NONCE_SIZE], - bool need_dirhash_key) +/* + * Sets up the fscrypt_common_info structure, and returns the relevant master + * key, if any, locked, for further type-specific processing. + */ +static int fscrypt_setup_common_info(struct fscrypt_common_info *crypt_info, + struct inode *inode, + const union fscrypt_policy *policy, + const u8 nonce[FSCRYPT_FILE_NONCE_SIZE], + fscrypt_ci_type_t type, + struct fscrypt_master_key **mk_ret) { - struct fscrypt_info *crypt_info; struct fscrypt_mode *mode; struct fscrypt_master_key *mk = NULL; int res; @@ -629,12 +642,10 @@ fscrypt_setup_encryption_info(struct inode *inode, if (res) return res; - crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_KERNEL); - if (!crypt_info) - return -ENOMEM; - crypt_info->ci_inode = inode; crypt_info->ci_policy = *policy; + crypt_info->ci_type = type; + memcpy(crypt_info->ci_nonce, nonce, FSCRYPT_FILE_NONCE_SIZE); mode = select_encryption_mode(&crypt_info->ci_policy, inode); @@ -653,7 +664,66 @@ fscrypt_setup_encryption_info(struct inode *inode, if (res) goto out; - res = fscrypt_setup_file_key(crypt_info, mk); + res = fscrypt_setup_info_key(crypt_info, mk); + if (res) + goto out; + + /* + * The IV_INO_LBLK_32 policy needs a hashed inode number, but new + * inodes may not have an inode number assigned yet. + */ + if (policy->version == FSCRYPT_POLICY_V2 && + (policy->v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) { + res = fscrypt_setup_ino_hash_key(mk); + if (res) + goto out; + + if (inode->i_ino) + fscrypt_hash_inode_number(crypt_info, mk); + } + + *mk_ret = mk; + return res; + +out: + if (mk) { + up_read(&mk->mk_sem); + fscrypt_put_master_key(mk); + } + return res; +} + +static void add_info_to_mk_decrypted_list(struct fscrypt_common_info *cci, + struct fscrypt_master_key *mk) +{ + if (mk) { + cci->ci_master_key = mk; + refcount_inc(&mk->mk_active_refs); + spin_lock(&mk->mk_decrypted_infos_lock); + list_add(&cci->ci_master_key_link, &mk->mk_decrypted_infos); + spin_unlock(&mk->mk_decrypted_infos_lock); + } +} + +static int +fscrypt_setup_encryption_info(struct inode *inode, + const union fscrypt_policy *policy, + const u8 nonce[FSCRYPT_FILE_NONCE_SIZE], + bool need_dirhash_key) +{ + struct fscrypt_info *crypt_inode_info; + struct fscrypt_common_info *crypt_info; + struct fscrypt_master_key *mk = NULL; + int res; + + crypt_inode_info = kmem_cache_zalloc(fscrypt_inode_info_cachep, + GFP_KERNEL); + if (!crypt_inode_info) + return -ENOMEM; + crypt_info = &crypt_inode_info->info; + + res = fscrypt_setup_common_info(crypt_info, inode, policy, nonce, + CI_INODE, &mk); if (res) goto out; @@ -668,23 +738,9 @@ fscrypt_setup_encryption_info(struct inode *inode, goto out; } - res = fscrypt_derive_dirhash_key(crypt_info, mk); - if (res) - goto out; - } - - /* - * The IV_INO_LBLK_32 policy needs a hashed inode number, but new - * inodes may not have an inode number assigned yet. - */ - if (policy->version == FSCRYPT_POLICY_V2 && - (policy->v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) { - res = fscrypt_setup_ino_hash_key(mk); + res = fscrypt_derive_dirhash_key(crypt_inode_info, mk); if (res) goto out; - - if (inode->i_ino) - fscrypt_hash_inode_number(crypt_info, mk); } /* @@ -693,20 +749,14 @@ fscrypt_setup_encryption_info(struct inode *inode, * fscrypt_get_info(). I.e., here we publish ->i_crypt_info with a * RELEASE barrier so that other tasks can ACQUIRE it. */ - if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) { + if (cmpxchg_release(&inode->i_crypt_info, NULL, + crypt_inode_info) == NULL) { /* * We won the race and set ->i_crypt_info to our crypt_info. * Now link it into the master key's inode list. */ - if (mk) { - crypt_info->ci_master_key = mk; - refcount_inc(&mk->mk_active_refs); - spin_lock(&mk->mk_decrypted_inodes_lock); - list_add(&crypt_info->ci_master_key_link, - &mk->mk_decrypted_inodes); - spin_unlock(&mk->mk_decrypted_inodes_lock); - } - crypt_info = NULL; + add_info_to_mk_decrypted_list(crypt_info, mk); + crypt_inode_info = NULL; } res = 0; out: @@ -714,7 +764,7 @@ fscrypt_setup_encryption_info(struct inode *inode, up_read(&mk->mk_sem); fscrypt_put_master_key(mk); } - put_crypt_info(crypt_info); + put_crypt_inode_info(crypt_inode_info); return res; } @@ -843,7 +893,7 @@ EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode); */ void fscrypt_put_encryption_info(struct inode *inode) { - put_crypt_info(inode->i_crypt_info); + put_crypt_inode_info(inode->i_crypt_info); inode->i_crypt_info = NULL; } EXPORT_SYMBOL(fscrypt_put_encryption_info); @@ -884,7 +934,7 @@ int fscrypt_drop_inode(struct inode *inode) * was provided via the legacy mechanism of the process-subscribed * keyrings, so we don't know whether it's been removed or not. */ - if (!ci || !ci->ci_master_key) + if (!ci || !ci->info.ci_master_key) return 0; /* @@ -904,6 +954,6 @@ int fscrypt_drop_inode(struct inode *inode) * then the thread removing the key will either evict the inode itself * or will correctly detect that it wasn't evicted due to the race. */ - return !is_master_key_secret_present(&ci->ci_master_key->mk_secret); + return !is_master_key_secret_present(&ci->info.ci_master_key->mk_secret); } EXPORT_SYMBOL_GPL(fscrypt_drop_inode); diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index 1e785cedead0..b57ed49ac201 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -9,7 +9,7 @@ * This file implements compatibility functions for the original encryption * policy version ("v1"), including: * - * - Deriving per-file encryption keys using the AES-128-ECB based KDF + * - Deriving per-info encryption keys using the AES-128-ECB based KDF * (rather than the new method of using HKDF-SHA512) * * - Retrieving fscrypt master keys from process-subscribed keyrings @@ -181,7 +181,8 @@ void fscrypt_put_direct_key(struct fscrypt_prepared_key *prep_key) */ static struct fscrypt_direct_key * find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, - const u8 *raw_key, const struct fscrypt_info *ci) + const u8 *raw_key, + const struct fscrypt_common_info *cci) { unsigned long hash_key; struct fscrypt_direct_key *dk; @@ -193,19 +194,19 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, */ BUILD_BUG_ON(sizeof(hash_key) > FSCRYPT_KEY_DESCRIPTOR_SIZE); - memcpy(&hash_key, ci->ci_policy.v1.master_key_descriptor, + memcpy(&hash_key, cci->ci_policy.v1.master_key_descriptor, sizeof(hash_key)); spin_lock(&fscrypt_direct_keys_lock); hash_for_each_possible(fscrypt_direct_keys, dk, dk_node, hash_key) { - if (memcmp(ci->ci_policy.v1.master_key_descriptor, + if (memcmp(cci->ci_policy.v1.master_key_descriptor, dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0) continue; - if (ci->ci_mode != dk->dk_mode) + if (cci->ci_mode != dk->dk_mode) continue; - if (!fscrypt_is_key_prepared(&dk->dk_key, ci)) + if (!fscrypt_is_key_prepared(&dk->dk_key, cci)) continue; - if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize)) + if (crypto_memneq(raw_key, dk->dk_raw, cci->ci_mode->keysize)) continue; /* using existing tfm with same (descriptor, mode, raw_key) */ refcount_inc(&dk->dk_refcount); @@ -221,13 +222,13 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, /* Prepare to encrypt directly using the master key in the given mode */ static struct fscrypt_direct_key * -fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key) +fscrypt_get_direct_key(const struct fscrypt_common_info *cci, const u8 *raw_key) { struct fscrypt_direct_key *dk; int err; /* Is there already a tfm for this key? */ - dk = find_or_insert_direct_key(NULL, raw_key, ci); + dk = find_or_insert_direct_key(NULL, raw_key, cci); if (dk) return dk; @@ -235,18 +236,18 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key) dk = kzalloc(sizeof(*dk), GFP_KERNEL); if (!dk) return ERR_PTR(-ENOMEM); - dk->dk_sb = ci->ci_inode->i_sb; + dk->dk_sb = cci->ci_inode->i_sb; refcount_set(&dk->dk_refcount, 1); - dk->dk_mode = ci->ci_mode; + dk->dk_mode = cci->ci_mode; dk->dk_key.type = FSCRYPT_KEY_DIRECT_V1; - err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci); + err = fscrypt_prepare_key(&dk->dk_key, raw_key, cci); if (err) goto err_free_dk; - memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor, + memcpy(dk->dk_descriptor, cci->ci_policy.v1.master_key_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE); - memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize); + memcpy(dk->dk_raw, raw_key, cci->ci_mode->keysize); - return find_or_insert_direct_key(dk, raw_key, ci); + return find_or_insert_direct_key(dk, raw_key, cci); err_free_dk: free_direct_key(dk); @@ -254,20 +255,20 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key) } /* v1 policy, DIRECT_KEY: use the master key directly */ -static int setup_v1_file_key_direct(struct fscrypt_info *ci, +static int setup_v1_info_key_direct(struct fscrypt_common_info *cci, const u8 *raw_master_key) { struct fscrypt_direct_key *dk; - dk = fscrypt_get_direct_key(ci, raw_master_key); + dk = fscrypt_get_direct_key(cci, raw_master_key); if (IS_ERR(dk)) return PTR_ERR(dk); - ci->ci_enc_key = &dk->dk_key; + cci->ci_enc_key = &dk->dk_key; return 0; } -/* v1 policy, !DIRECT_KEY: derive the file's encryption key */ -static int setup_v1_file_key_derived(struct fscrypt_info *ci, +/* v1 policy, !DIRECT_KEY: derive the info's encryption key */ +static int setup_v1_info_key_derived(struct fscrypt_common_info *cci, const u8 *raw_master_key) { u8 *derived_key; @@ -277,47 +278,48 @@ static int setup_v1_file_key_derived(struct fscrypt_info *ci, * This cannot be a stack buffer because it will be passed to the * scatterlist crypto API during derive_key_aes(). */ - derived_key = kmalloc(ci->ci_mode->keysize, GFP_KERNEL); + derived_key = kmalloc(cci->ci_mode->keysize, GFP_KERNEL); if (!derived_key) return -ENOMEM; - err = derive_key_aes(raw_master_key, ci->ci_nonce, - derived_key, ci->ci_mode->keysize); + err = derive_key_aes(raw_master_key, cci->ci_nonce, + derived_key, cci->ci_mode->keysize); if (err) goto out; - err = fscrypt_set_per_file_enc_key(ci, derived_key); + err = fscrypt_set_per_info_enc_key(cci, derived_key); out: kfree_sensitive(derived_key); return err; } -int fscrypt_setup_v1_file_key(struct fscrypt_info *ci, const u8 *raw_master_key) +int fscrypt_setup_v1_info_key(struct fscrypt_common_info *cci, + const u8 *raw_master_key) { - if (ci->ci_policy.v1.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) - return setup_v1_file_key_direct(ci, raw_master_key); + if (cci->ci_policy.v1.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) + return setup_v1_info_key_direct(cci, raw_master_key); else - return setup_v1_file_key_derived(ci, raw_master_key); + return setup_v1_info_key_derived(cci, raw_master_key); } -int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci) +int fscrypt_setup_v1_info_key_via_subscribed_keyrings(struct fscrypt_common_info *cci) { struct key *key; const struct fscrypt_key *payload; int err; key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX, - ci->ci_policy.v1.master_key_descriptor, - ci->ci_mode->keysize, &payload); - if (key == ERR_PTR(-ENOKEY) && ci->ci_inode->i_sb->s_cop->key_prefix) { - key = find_and_lock_process_key(ci->ci_inode->i_sb->s_cop->key_prefix, - ci->ci_policy.v1.master_key_descriptor, - ci->ci_mode->keysize, &payload); + cci->ci_policy.v1.master_key_descriptor, + cci->ci_mode->keysize, &payload); + if (key == ERR_PTR(-ENOKEY) && cci->ci_inode->i_sb->s_cop->key_prefix) { + key = find_and_lock_process_key(cci->ci_inode->i_sb->s_cop->key_prefix, + cci->ci_policy.v1.master_key_descriptor, + cci->ci_mode->keysize, &payload); } if (IS_ERR(key)) return PTR_ERR(key); - err = fscrypt_setup_v1_file_key(ci, payload->raw); + err = fscrypt_setup_v1_info_key(cci, payload->raw); up_read(&key->sem); key_put(key); return err; diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index f4456ecb3f87..ceb648669832 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -412,7 +412,7 @@ static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy) ci = fscrypt_get_info(inode); if (ci) { /* key available, use the cached policy */ - *policy = ci->ci_policy; + *policy = ci->info.ci_policy; return 0; } @@ -698,7 +698,7 @@ const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir) err = fscrypt_require_key(dir); if (err) return ERR_PTR(err); - return &dir->i_crypt_info->ci_policy; + return &dir->i_crypt_info->info.ci_policy; } return fscrypt_get_dummy_policy(dir->i_sb); @@ -726,7 +726,7 @@ int fscrypt_context_for_new_inode(void *ctx, struct inode *inode) if (WARN_ON_ONCE(!ci)) return -ENOKEY; - return fscrypt_new_context(ctx, &ci->ci_policy, ci->ci_nonce); + return fscrypt_new_context(ctx, &ci->info.ci_policy, ci->info.ci_nonce); } EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode); @@ -743,6 +743,7 @@ EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode); int fscrypt_set_context(struct inode *inode, void *fs_data) { struct fscrypt_info *ci = inode->i_crypt_info; + struct fscrypt_common_info *cci = &ci->info; union fscrypt_context ctx; int ctxsize; @@ -754,9 +755,9 @@ int fscrypt_set_context(struct inode *inode, void *fs_data) * This may be the first time the inode number is available, so do any * delayed key setup that requires the inode number. */ - if (ci->ci_policy.version == FSCRYPT_POLICY_V2 && - (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) - fscrypt_hash_inode_number(ci, ci->ci_master_key); + if (cci->ci_policy.version == FSCRYPT_POLICY_V2 && + (cci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) + fscrypt_hash_inode_number(cci, cci->ci_master_key); return inode->i_sb->s_cop->set_context(inode, &ctx, ctxsize, fs_data); } From patchwork Sat Sep 2 05:54:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373022 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C493BCA0FFC for ; Sat, 2 Sep 2023 05:56:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351613AbjIBF4B (ORCPT ); Sat, 2 Sep 2023 01:56:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32958 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230215AbjIBF4B (ORCPT ); Sat, 2 Sep 2023 01:56:01 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EFD8E10F5; Fri, 1 Sep 2023 22:55:57 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 5206E803B8; Sat, 2 Sep 2023 01:55:57 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634157; bh=fiVfVaVeSO8+LySxAwlXQQui2JcU6Eza8j+LxYs3Nmk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Sad7seUUZDFSagU6zNxRvOfHcLYcFTFtetX1d5k4LkRk9Br4FlEdH8UjjUKClaQQV tzblnBeRBNdJRtAX+z8gv7sBGm05e9Ag+QsrlAqfrOipIdjryuLlTQc+s2LdWcElbx 4FBDjhoQOTI18U9G4noc4BkAspiqXqoNwiuJWavrbWVwLsHWX0h8Y7Lz1yaes8qBFz 5R86mnv3h52SQELApB03aVOtC2LFPMj9Pi9e21F2Lk3rtxu8gCL5RAJRgX3l2sHMIT A9Z6He4NVfZi0mEutvvlYHpCkou8Mo8tsdf39OtTgmLIzUuaGhni+o1DWd/aSbMWfz wADXuNc2T9gnw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 05/13] fscrypt: add creation/usage/freeing of per-extent infos Date: Sat, 2 Sep 2023 01:54:23 -0400 Message-ID: <7045a2f0f411494e53b6ef1f995bd0e4cfc73f17.1693630890.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org This change adds the superblock function pointer to get the info corresponding to a specific block in an inode for a filesystem using per-extent infos. It allows creating a info for a new extent and freeing that info, and uses the extent's info if appropriate in encrypting blocks of data. It also makes sure that the return value of fscrypt_get_lblk_info() is non-NULL before using it, since there's no longer a mechanical guarantee that we'll never call fscrypt_get_lblk_info() without having the relevant info loaded. We *oughtn't*, but we're not explicitly checking that it's loaded before these points. This change does not deal with saving and loading an extent's info, but introduces the mechanics necessary therefore. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 14 +++++- fs/crypto/fscrypt_private.h | 53 ++++++++++++++++++-- fs/crypto/inline_crypt.c | 5 ++ fs/crypto/keysetup.c | 97 +++++++++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 41 ++++++++++++++++ 5 files changed, 204 insertions(+), 6 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index d5c9326a1919..5a93c30e6b86 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -40,6 +40,7 @@ static struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); struct kmem_cache *fscrypt_inode_info_cachep; +struct kmem_cache *fscrypt_extent_info_cachep; void fscrypt_enqueue_decrypt_work(struct work_struct *work) { @@ -113,6 +114,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, struct crypto_skcipher *tfm = cci->ci_enc_key->tfm; int res = 0; + if (WARN_ON_ONCE(!cci)) + return -EINVAL; if (WARN_ON_ONCE(len <= 0)) return -EINVAL; if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0)) @@ -397,13 +400,20 @@ static int __init fscrypt_init(void) if (!fscrypt_inode_info_cachep) goto fail_free_queue; + fscrypt_extent_info_cachep = KMEM_CACHE(fscrypt_extent_info, + SLAB_RECLAIM_ACCOUNT); + if (!fscrypt_extent_info_cachep) + goto fail_free_inode_info; + err = fscrypt_init_keyring(); if (err) - goto fail_free_info; + goto fail_free_extent_info; return 0; -fail_free_info: +fail_free_extent_info: + kmem_cache_destroy(fscrypt_extent_info_cachep); +fail_free_inode_info: kmem_cache_destroy(fscrypt_inode_info_cachep); fail_free_queue: destroy_workqueue(fscrypt_read_workqueue); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cc1a61ade2a4..9320428f8915 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -294,13 +294,47 @@ struct fscrypt_info { }; /* + * fscrypt_extent_info - the "encryption key" for an extent */ +struct fscrypt_extent_info { + struct fscrypt_common_info info; +}; typedef enum { FS_DECRYPT = 0, FS_ENCRYPT, } fscrypt_direction_t; +/** + * fscrypt_fs_uses_extent_encryption() -- whether a filesystem uses extent + * encryption + * + * @sb: the superblock of the relevant filesystem + * + * Return: true if the fs uses extent encryption, else false + */ +static inline bool +fscrypt_fs_uses_extent_encryption(const struct super_block *sb) +{ + return !!sb->s_cop->get_extent_info; +} + +/** + * fscrypt_uses_extent_encryption() -- whether an inode uses extent encryption + * + * @inode: the inode in question + * + * Return: true if the inode uses extent encryption, else false + */ +static inline bool fscrypt_uses_extent_encryption(const struct inode *inode) +{ + /* Non-regular files don't have extents. */ + if (!S_ISREG(inode->i_mode)) + return false; + + return fscrypt_fs_uses_extent_encryption(inode->i_sb); +} + /** * fscrypt_get_lblk_info() - get the fscrypt_common_info to crypt a particular * block @@ -313,15 +347,26 @@ typedef enum { * always be @lblk; for extent-based encryption, this will be in * the range [0, lblk]. Can be NULL * @extent_len: a pointer to return the minimum number of lblks starting at - * this offset which also belong to the same fscrypt_info. Can be - * NULL + * this offset which also belong to the same fscrypt_common_info. + * Can be NULL * - * Return: the appropriate fscrypt_info if there is one, else NULL. + * Return: the appropriate fscrypt_common_info if there is one, else NULL. */ static inline struct fscrypt_common_info * fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset, u64 *extent_len) { + if (fscrypt_uses_extent_encryption(inode)) { + struct fscrypt_extent_info *cei; + int res; + + res = inode->i_sb->s_cop->get_extent_info(inode, lblk, &cei, + offset, extent_len); + if (res == 0) + return &cei->info; + return NULL; + } + if (offset) *offset = lblk; if (extent_len) @@ -330,9 +375,9 @@ fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset, return &inode->i_crypt_info->info; } - /* crypto.c */ extern struct kmem_cache *fscrypt_inode_info_cachep; +extern struct kmem_cache *fscrypt_extent_info_cachep; int fscrypt_initialize(struct super_block *sb); int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, u64 lblk_num, struct page *src_page, diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 09ec82b8a98a..f0229234249c 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -272,6 +272,8 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, if (!fscrypt_inode_uses_inline_crypto(inode)) return; cci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL); + if (!cci) + return; fscrypt_generate_dun(cci, ci_offset, dun); bio_crypt_set_ctx(bio, cci->ci_enc_key->blk_key, dun, gfp_mask); @@ -359,6 +361,9 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, return true; cci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL); + if (!cci) + return false; + /* * Comparing the key pointers is good enough, as all I/O for each key * uses the same pointer. I.e., there's currently no need to support diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index ae250e432dcd..c9c16acf4c9b 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -957,3 +957,100 @@ int fscrypt_drop_inode(struct inode *inode) return !is_master_key_secret_present(&ci->info.ci_master_key->mk_secret); } EXPORT_SYMBOL_GPL(fscrypt_drop_inode); + +static void put_crypt_extent_info(struct fscrypt_extent_info *ci) +{ + if (!ci) + return; + + free_prepared_key(&ci->info); + remove_info_from_mk_decrypted_list(&ci->info); + + memzero_explicit(ci, sizeof(*ci)); + kmem_cache_free(fscrypt_extent_info_cachep, ci); +} + +static int +fscrypt_setup_extent_info(struct inode *inode, + const union fscrypt_policy *policy, + const u8 nonce[FSCRYPT_FILE_NONCE_SIZE], + struct fscrypt_extent_info **info_ptr) +{ + struct fscrypt_extent_info *crypt_extent_info; + struct fscrypt_common_info *crypt_info; + struct fscrypt_master_key *mk = NULL; + int res; + + crypt_extent_info = kmem_cache_zalloc(fscrypt_extent_info_cachep, + GFP_KERNEL); + if (!crypt_extent_info) + return -ENOMEM; + crypt_info = &crypt_extent_info->info; + + res = fscrypt_setup_common_info(crypt_info, inode, policy, nonce, + CI_EXTENT, &mk); + if (res) + goto out; + + *info_ptr = crypt_extent_info; + add_info_to_mk_decrypted_list(crypt_info, mk); + + crypt_extent_info = NULL; + res = 0; +out: + if (mk) { + up_read(&mk->mk_sem); + fscrypt_put_master_key(mk); + } + put_crypt_extent_info(crypt_extent_info); + return res; +} + +/** + * fscrypt_prepare_new_extent() - set up the fscrypt_extent_info for a new extent + * @inode: the inode to which the extent belongs + * @info_ptr: a pointer to return the extent's fscrypt_extent_info into + * * + * If the extent is part of an encrypted inode, set up a fscrypt_extent_info in + * preparation for encrypting data. + * + * This isn't %GFP_NOFS-safe. + * + * This doesn't persist the new extent's encryption context. That still needs to + * be done later by calling fscrypt_set_extent_context(). + * + * Return: 0 on success, -ENOKEY if the encryption key is missing, or another + * -errno code + */ +int fscrypt_prepare_new_extent(struct inode *inode, + struct fscrypt_extent_info **info_ptr) +{ + const union fscrypt_policy *policy; + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; + + policy = fscrypt_policy_to_inherit(inode); + if (policy == NULL) + return 0; + if (IS_ERR(policy)) + return PTR_ERR(policy); + + /* Only regular files can have extents. */ + if (WARN_ON_ONCE(!S_ISREG(inode->i_mode))) + return -EINVAL; + + get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE); + return fscrypt_setup_extent_info(inode, policy, nonce, + info_ptr); +} +EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent); + +/** + * fscrypt_free_extent_info() - free an extent's fscrypt_extent_info + * @info_ptr: a pointer containing the extent's fscrypt_extent_info pointer. + */ +void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr) +{ + put_crypt_extent_info(*info_ptr); + *info_ptr = NULL; +} +EXPORT_SYMBOL_GPL(fscrypt_free_extent_info); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index c895b12737a1..cc5de5ec888c 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -32,6 +32,7 @@ union fscrypt_policy; struct fscrypt_info; +struct fscrypt_extent_info; struct fs_parameter; struct seq_file; @@ -113,6 +114,29 @@ struct fscrypt_operations { int (*set_context)(struct inode *inode, const void *ctx, size_t len, void *fs_data); + /* + * Get the fscrypt_info for the given inode at the given block, for + * extent-based encryption only. + * + * @inode: the inode in question + * @lblk: the logical block number in question + * @ci: a pointer to return the fscrypt_extent_info + * @offset: a pointer to return the offset of @lblk into the extent, + * in blocks (may be NULL) + * @extent_len: a pointer to return the number of blocks in this extent + * starting at this point (may be NULL) + * + * May cause the filesystem to allocate memory, which the filesystem + * must do with %GFP_NOFS, including calls into fscrypt to create or + * load an fscrypt_info. + * + * Return: 0 if an extent is found with an info, -ENODATA if the key is + * unavailable, or another -errno. + */ + int (*get_extent_info)(const struct inode *inode, u64 lblk, + struct fscrypt_extent_info **ci, u64 *offset, + u64 *extent_len); + /* * Get the dummy fscrypt policy in use on the filesystem (if any). * @@ -330,6 +354,10 @@ void fscrypt_put_encryption_info(struct inode *inode); void fscrypt_free_inode(struct inode *inode); int fscrypt_drop_inode(struct inode *inode); +int fscrypt_prepare_new_extent(struct inode *inode, + struct fscrypt_extent_info **info_ptr); +void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr); + /* fname.c */ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, u8 *out, unsigned int olen); @@ -591,6 +619,19 @@ static inline int fscrypt_drop_inode(struct inode *inode) return 0; } +static inline int +fscrypt_prepare_new_extent(struct inode *inode, + struct fscrypt_extent_info **info_ptr) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + +static inline void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr) +{ +} + /* fname.c */ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, From patchwork Sat Sep 2 05:54:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373023 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E9864CA0FFE for ; Sat, 2 Sep 2023 05:56:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351620AbjIBF4E (ORCPT ); Sat, 2 Sep 2023 01:56:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48820 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343615AbjIBF4E (ORCPT ); Sat, 2 Sep 2023 01:56:04 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0A4AF10F4; Fri, 1 Sep 2023 22:56:00 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 727D3803B3; Sat, 2 Sep 2023 01:56:00 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634160; bh=KGmOigNZmiCYiYlflwG54i9oGQLZG48bRqPKe2ArkoA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YZ3boiZGzT0M9TFS1Jt360WK3yXhhnHy7KsRIWwtq8ozd+gFTMsVukkezahguV+55 2IRRcW6W4wxj3MleYDUmJh9OPk5GSEtzFuJhDLqFdFLcu0+vdWvciknYsQjM3kGD1F 6f6HrUaYj1RIvpYB98Hf21I9ycGcd3FnSjUwPOkUrhDyvPrJZIQeFf6TFk0WbsYC9t i4lHAfB/VnzP/2HAXloOTuPJh9/D25DIIacvktFZQp5v1AqIrtlC01xuPfMdlxWIP8 oVBO37zMksniUO85CJboQH2SA0T1l/cHjdSsXJBklKAVaGDjcDssdtfEOgS8euMDqC gxJsYbaZ0LWHw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 06/13] fscrypt: allow load/save of extent contexts Date: Sat, 2 Sep 2023 01:54:24 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org The other half of using per-extent infos is saving and loading them from disk. This is the one change which cares about whether a lightweight or heavyweight extent context is stored on disk. This implements the lightweight version. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/keysetup.c | 39 +++++++++++++++++++++++++++++++++++++++ fs/crypto/policy.c | 22 ++++++++++++++++++++++ include/linux/fscrypt.h | 19 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index c9c16acf4c9b..90143377cc61 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -1044,6 +1044,45 @@ int fscrypt_prepare_new_extent(struct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent); +/** + * fscrypt_load_extent_info() - load a preexisting extent's fscrypt_extent_info + * @inode: the inode to which the extent belongs. Must be encrypted. + * @buf: a buffer containing the extent's stored context + * @len: the length of the @ctx buffer + * @info_ptr: a pointer to return the extent's fscrypt_extent_info into + * + * This is not %GFP_NOFS safe, so the caller is expected to call + * memalloc_nofs_save/restore() if appropriate. + * + * Return: 0 if successful, or -errno if it fails. + */ +int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len, + struct fscrypt_extent_info **info_ptr) +{ + int res; + union fscrypt_context ctx; + const union fscrypt_policy *policy; + + if (!fscrypt_has_encryption_key(inode)) + return -EINVAL; + + if (len != FSCRYPT_FILE_NONCE_SIZE) { + fscrypt_warn(inode, + "Unrecognized or corrupt encryption context"); + return -EINVAL; + } + + policy = fscrypt_policy_to_inherit(inode); + if (policy == NULL) + return 0; + if (IS_ERR(policy)) + return PTR_ERR(policy); + + return fscrypt_setup_extent_info(inode, policy, buf, + info_ptr); +} +EXPORT_SYMBOL_GPL(fscrypt_load_extent_info); + /** * fscrypt_free_extent_info() - free an extent's fscrypt_extent_info * @info_ptr: a pointer containing the extent's fscrypt_extent_info pointer. diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index ceb648669832..cfbe83aee847 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -763,6 +763,28 @@ int fscrypt_set_context(struct inode *inode, void *fs_data) } EXPORT_SYMBOL_GPL(fscrypt_set_context); +/** + * fscrypt_set_extent_context() - Set the fscrypt extent context for an extent + * @ci: info from which to fetch policy and nonce + * @ctx: where context should be written + * @len: the size of ctx + * + * Given an fscrypt_extent_info belonging to an extent (generated via + * fscrypt_prepare_new_extent()), generate a new context and write it to @ctx. + * len is checked to be at least FSCRYPT_EXTENT_CONTEXT_MAX_SIZE bytes. + * + * Return: size of the resulting context or a negative error code. + */ +int fscrypt_set_extent_context(struct fscrypt_extent_info *ci, void *ctx, + size_t len) +{ + if (len < FSCRYPT_EXTENT_CONTEXT_MAX_SIZE) + return -EINVAL; + memcpy(ctx, ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE); + return FSCRYPT_FILE_NONCE_SIZE; +} +EXPORT_SYMBOL_GPL(fscrypt_set_extent_context); + /** * fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option * @param: the mount option diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index cc5de5ec888c..b57fc5645076 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -57,6 +57,7 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +#define FSCRYPT_EXTENT_CONTEXT_MAX_SIZE 16 #ifdef CONFIG_FS_ENCRYPTION @@ -317,6 +318,8 @@ int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child); int fscrypt_context_for_new_inode(void *ctx, struct inode *inode); int fscrypt_set_context(struct inode *inode, void *fs_data); +int fscrypt_set_extent_context(struct fscrypt_extent_info *info, void *ctx, + size_t len); struct fscrypt_dummy_policy { const union fscrypt_policy *policy; @@ -357,6 +360,9 @@ int fscrypt_drop_inode(struct inode *inode); int fscrypt_prepare_new_extent(struct inode *inode, struct fscrypt_extent_info **info_ptr); void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr); +int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len, + struct fscrypt_extent_info **info_ptr); + /* fname.c */ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, @@ -533,6 +539,12 @@ static inline int fscrypt_set_context(struct inode *inode, void *fs_data) return -EOPNOTSUPP; } +static inline int fscrypt_set_extent_context(struct fscrypt_info *info, + void *ctx, size_t len) +{ + return -EOPNOTSUPP; +} + struct fscrypt_dummy_policy { }; @@ -632,6 +644,13 @@ static inline void fscrypt_free_extent_info(struct fscrypt_extent_info **info_pt { } +static inline int fscrypt_load_extent_info(struct inode *inode, void *buf, + size_t len, + struct fscrypt_info **info_ptr) +{ + return -EOPNOTSUPP; +} + /* fname.c */ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, From patchwork Sat Sep 2 05:54:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD20BCA0FF8 for ; Sat, 2 Sep 2023 05:56:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351623AbjIBF4G (ORCPT ); Sat, 2 Sep 2023 01:56:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48832 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343615AbjIBF4F (ORCPT ); Sat, 2 Sep 2023 01:56:05 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1CD1810F4; Fri, 1 Sep 2023 22:56:03 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 7C1AA803B8; Sat, 2 Sep 2023 01:56:02 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634162; bh=pNWZ9FSgIarpGMwNEZ5zwQ/hDUlwdOXDex/Vx8GeIk8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pllgTrlUtxSzjTR/8jAxq225mCXG+YcwEhheJ8z2zdiFPYLnOym3cPce7KJxqqiXy 3Uq/vNKO3M97fAuytDP1+KLIWtEGnMr3rc4p1yuDhnqXcbG+dKDXgkb102r4nt5JxU WtkkXw6FFRNGWZRBic7MnJ3SuAdxwhDlX8eNyrlr60qZYV7U4Lu9u4fszr1ynzg0Kv 6VRCqceDfmyQyu8bX6ExdbFMlBDe+iqNYzkKU3o6IFj0N6Jz2liI9Xne5KbmyGMMAy eJpnFq5Crzh620R+WhlH//yNkRHVolw+1ZHsz0MpEKDOgNfgHGohNxViTi6pdb+dEa fVJy9hCdJ7riw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent Date: Sat, 2 Sep 2023 01:54:25 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org For contrast purposes, this patch contains the entirety of the changes necessary to switch between lightweight and heavyweight extents. This patch could be dropped, or rolled into the former change, without changing anything else. Lightweight extents relying on their parent inode's context for key and policy information do take up less disk space. Additionally, they guarantee that if inode open succeeds, then all extents will be readable and writeable, matching the current inode-based fscrypt behavior. However, heavyweight extents permit greater flexibility for future extensions: - Any form of changing the key for a non-empty directory's future writes requires that extents have some sort of policy in addition to the nonce, which is essentially the contents of the full fscrypt_context. - This could be approximated using overlayfs writing to a new encrypted directory, but this would waste space used by overwritten data and makes it very difficult to have nested subvolumes each with their own key, so it's very preferable to support this natively in btrfs. - Scrub (verifying checksums) currently iterates over extents, without interacting with inodes; in an authenticated encryption world, scrub verifying authentication tags would need to iterate over inodes (a large departure from the present) or need heavyweight extents storing the necessary key information. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/keysetup.c | 20 ++++++++++---------- fs/crypto/policy.c | 5 ++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 90143377cc61..4146b1380cb5 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -1061,25 +1061,25 @@ int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len, { int res; union fscrypt_context ctx; - const union fscrypt_policy *policy; + union fscrypt_policy policy; if (!fscrypt_has_encryption_key(inode)) return -EINVAL; - if (len != FSCRYPT_FILE_NONCE_SIZE) { + memcpy(&ctx, buf, len); + + res = fscrypt_policy_from_context(&policy, &ctx, len); + if (res) { fscrypt_warn(inode, "Unrecognized or corrupt encryption context"); - return -EINVAL; + return res; } - policy = fscrypt_policy_to_inherit(inode); - if (policy == NULL) - return 0; - if (IS_ERR(policy)) - return PTR_ERR(policy); + if (!fscrypt_supported_policy(&policy, inode)) + return -EINVAL; - return fscrypt_setup_extent_info(inode, policy, buf, - info_ptr); + return fscrypt_setup_extent_info(inode, &policy, + fscrypt_context_nonce(&ctx), info_ptr); } EXPORT_SYMBOL_GPL(fscrypt_load_extent_info); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index cfbe83aee847..314bb6e97cec 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -778,10 +778,9 @@ EXPORT_SYMBOL_GPL(fscrypt_set_context); int fscrypt_set_extent_context(struct fscrypt_extent_info *ci, void *ctx, size_t len) { - if (len < FSCRYPT_EXTENT_CONTEXT_MAX_SIZE) + if (len < FSCRYPT_SET_CONTEXT_MAX_SIZE) return -EINVAL; - memcpy(ctx, ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE); - return FSCRYPT_FILE_NONCE_SIZE; + return fscrypt_new_context(ctx, &ci->info.ci_policy, ci->info.ci_nonce); } EXPORT_SYMBOL_GPL(fscrypt_set_extent_context); From patchwork Sat Sep 2 05:54:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EC61ECA1002 for ; Sat, 2 Sep 2023 05:56:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351627AbjIBF4I (ORCPT ); Sat, 2 Sep 2023 01:56:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48854 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343615AbjIBF4I (ORCPT ); Sat, 2 Sep 2023 01:56:08 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3FF2610F6; Fri, 1 Sep 2023 22:56:05 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 97D2B803B3; Sat, 2 Sep 2023 01:56:04 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634165; bh=sjWPfktkz8U2Au2Wkmak2i4sH8J6VID8oPHMkxQ2yGI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nvst1U6FgIWYDBkxUlcrZlYIIVsihu2GkWqBIb4O++VEX8mc9erQJ261K9garY3AI pX8L3DfOmlC2+ztsuvgvogOmxASUfhlBbb5oK2OTJOgn/Nr937h0mH0nmeJMWdyGtg 0p/m/OK+LT7UO/0Ofy7PzyOuVpfnLz+ugN+B+2RfF9n0Yx3lAHpjzy2EX2888v2BJT kf9icbC4BTVbZLM8B5t9KN2y32hRR84LD2IvfqkDlhZjS5UqZFwIL9VLShYYPmc7Np 8uO+GJlCSPUxoS2r4LwviPj0vO4hGQae3fd4FG/a03l7a6lm7H3uGjQxln6HdJMYKb JD1l9Lbioc/xg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 08/13] fscrypt: save session key credentials for extent infos Date: Sat, 2 Sep 2023 01:54:26 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org For v1 encryption policies using per-session keys, the thread which opens the inode and therefore initializes the encryption info is part of the session, so it can get the key from the session keyring. However, for extent encryption, the extent infos are likely loaded from a different thread, which does not have access to the session keyring. This change saves the credentials of the inode opening thread and reuses those credentials temporarily when dealing with extent infos, allowing finding the encryption key correctly. v1 encryption policies using per-session keys should probably not exist for new usages such as extent encryption, but this makes more tests work without change; maybe the right answer is to disallow v1 session keys plus extent encryption and deal with editing tests to not use v1 session encryption so much. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/fscrypt_private.h | 9 +++++++++ fs/crypto/keysetup.c | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 9320428f8915..fe48b61a524b 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -284,6 +284,15 @@ struct fscrypt_common_info { struct fscrypt_info { struct fscrypt_common_info info; + /* Credential struct from the thread which created this info. This is + * only used in v1 session keyrings with extent encryption; it allows + * the thread creating extents for an inode to join the session + * keyring temporarily, since otherwise the thread is usually part of + * kernel writeback and therefore unrelated to the thread with the + * right session key. + */ + struct cred *ci_session_creds; + /* * This inode's hash key for filenames. This is a 128-bit SipHash-2-4 * key. This is only set for directories that use a keyed dirhash over diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 4146b1380cb5..5e944ec4e36f 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -619,6 +619,9 @@ static void put_crypt_inode_info(struct fscrypt_info *ci) free_prepared_key(&ci->info); remove_info_from_mk_decrypted_list(&ci->info); + if (ci->ci_session_creds) + abort_creds(ci->ci_session_creds); + memzero_explicit(ci, sizeof(*ci)); kmem_cache_free(fscrypt_inode_info_cachep, ci); } @@ -727,6 +730,9 @@ fscrypt_setup_encryption_info(struct inode *inode, if (res) goto out; + if (!mk) + crypt_inode_info->ci_session_creds = prepare_creds(); + /* * Derive a secret dirhash key for directories that need it. It * should be impossible to set flags such that a v1 policy sets @@ -979,6 +985,7 @@ fscrypt_setup_extent_info(struct inode *inode, struct fscrypt_extent_info *crypt_extent_info; struct fscrypt_common_info *crypt_info; struct fscrypt_master_key *mk = NULL; + const struct cred *creds = NULL; int res; crypt_extent_info = kmem_cache_zalloc(fscrypt_extent_info_cachep, @@ -987,8 +994,20 @@ fscrypt_setup_extent_info(struct inode *inode, return -ENOMEM; crypt_info = &crypt_extent_info->info; + if (inode->i_crypt_info->ci_session_creds) { + /* + * The inode this is being created for is using a session key, + * so we have to join this thread to that session temporarily + * in order to be able to find the right key... + */ + creds = override_creds(inode->i_crypt_info->ci_session_creds); + } + res = fscrypt_setup_common_info(crypt_info, inode, policy, nonce, CI_EXTENT, &mk); + if (creds) + revert_creds(creds); + if (res) goto out; From patchwork Sat Sep 2 05:54:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373027 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7AEA5CA0FFE for ; Sat, 2 Sep 2023 05:56:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351628AbjIBF4K (ORCPT ); Sat, 2 Sep 2023 01:56:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48916 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243397AbjIBF4K (ORCPT ); Sat, 2 Sep 2023 01:56:10 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 36A6710F6; Fri, 1 Sep 2023 22:56:07 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 9FFDE80A27; Sat, 2 Sep 2023 01:56:06 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634167; bh=Onf9UxFarwFjlBaDiO4ibgiZHvS0BNRZ/Y0HdfEcyLg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wbwDV1uXLavuH15YZeQb7cW+FHG8y6zDskfNrvj+J3qBQrzmHis2X9gYSRh1cUzuO Efy3JXY0zycXksqRgCe4fbquA2LDjrGyuMlIGV8g4QuifdcMCibmGkl+CroN1DOOw7 s0cUyNOJx3n9G4i0j3HyjaxkmJiglznmZg/96Io0XBTTyoW7FgaiLqgK/rlxVerrMD QhsD3QbLXuQHOySzQcIWghVEHjFev6O2cdUK0zlKdBBf+lPtQKCPlkoe53ibq57s8G MMcMfy0EvMxmKFcsn8ZlJ8nnb0/+G2h7JSyVqY3jr0/mQ5FsiS8ifaQnnvqXzJERjD SOHCk859/WhIw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 09/13] fscrypt: revamp key removal for extent encryption Date: Sat, 2 Sep 2023 01:54:27 -0400 Message-ID: <69acd3cd235b4b1bbf414b35e79c5a2131a5de95.1693630890.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Currently, for inode encryption, once an inode is open IO will not fail due to lack of key until the inode is closed. Even if the key is removed, open inodes will continue to use a tfm or inline crypt context set up with the key. For extent encryption, it's a little harder, since the extent may not be created or loaded until well after the REMOVE_KEY ioctl is called. If the key is actually fully removed, then the extent will be unable to load/create, since it has to set up a new inline crypt context using the key. Therefore, make key removal 'soft' for extent-based encryption: keep the key material around if any inodes using extent encryption are using it, allowing extents for those inodes to use the key material. Currently, both the key secret and each inode using the key keep a reference to the structure; when the remove ioctl is called, the key secret is removed and its reference is dropped. However, if we need to keep the key secret around, we can't wipe the secret there, so to preserve the invariant, we move both wiping and dropping the secret's reference to the last inode releasing a soft-deleted key. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/fscrypt_private.h | 22 ++++++++++++++++----- fs/crypto/keyring.c | 39 +++++++++++++++++++++++++++---------- fs/crypto/keysetup.c | 35 +++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 17 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index fe48b61a524b..dd7740105264 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -591,11 +591,14 @@ struct fscrypt_master_key { /* * The secret key material. After FS_IOC_REMOVE_ENCRYPTION_KEY is - * executed, this is wiped and no new inodes can be unlocked with this - * key; however, there may still be inodes in ->mk_decrypted_infos - * which could not be evicted. As long as some inodes still remain, - * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or - * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again. + * executed, no new inodes can be unlocked with this key; however, + * there may still be inodes in ->mk_decrypted_infos which could not + * be evicted. For inode-based encryption, the secret is wiped; for + * extent-based encryption, the secret is preserved while inodes still + * reference it, as they may need to create new extents using the + * secret to service IO; @soft_deleted is set to true then. As long as + * some inodes still remain, FS_IOC_REMOVE_ENCRYPTION_KEY can be + * retried, or FS_IOC_ADD_ENCRYPTION_KEY can add the secret again. * * While ->mk_secret is present, one ref in ->mk_active_refs is held. * @@ -634,6 +637,13 @@ struct fscrypt_master_key { struct list_head mk_decrypted_infos; spinlock_t mk_decrypted_infos_lock; + /* + * Whether the key is unavailable to new inodes, but still available + * to new extents within decrypted inodes. Protected by ->mk_sem, except + * for race-okay access in fscrypt_drop_inode(). + */ + bool mk_soft_deleted; + /* * Per-mode encryption keys for the various types of encryption policies * that use them. Allocated and derived on-demand. @@ -661,6 +671,8 @@ is_master_key_secret_present(const struct fscrypt_master_key_secret *secret) return READ_ONCE(secret->size) != 0; } +void fscrypt_wipe_master_key_secret(struct fscrypt_master_key_secret *secret); + static inline const char *master_key_spec_type( const struct fscrypt_key_specifier *spec) { diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 27ae0345fa85..9235a5a9bcba 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -38,7 +38,7 @@ struct fscrypt_keyring { struct hlist_head key_hashtable[128]; }; -static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret) +void fscrypt_wipe_master_key_secret(struct fscrypt_master_key_secret *secret) { fscrypt_destroy_hkdf(&secret->hkdf); memzero_explicit(secret, sizeof(*secret)); @@ -239,8 +239,9 @@ void fscrypt_destroy_keyring(struct super_block *sb) */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 1); WARN_ON_ONCE(refcount_read(&mk->mk_struct_refs) != 1); - WARN_ON_ONCE(!is_master_key_secret_present(&mk->mk_secret)); - wipe_master_key_secret(&mk->mk_secret); + WARN_ON_ONCE(!mk->mk_soft_deleted && + !is_master_key_secret_present(&mk->mk_secret)); + fscrypt_wipe_master_key_secret(&mk->mk_secret); fscrypt_put_master_key_activeref(sb, mk); } } @@ -485,6 +486,8 @@ static int add_existing_master_key(struct fscrypt_master_key *mk, move_master_key_secret(&mk->mk_secret, secret); } + mk->mk_soft_deleted = false; + return 0; } @@ -738,7 +741,7 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) goto out_wipe_secret; err = 0; out_wipe_secret: - wipe_master_key_secret(&secret); + fscrypt_wipe_master_key_secret(&secret); return err; } EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); @@ -770,7 +773,7 @@ int fscrypt_get_test_dummy_key_identifier( NULL, 0, key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE); out: - wipe_master_key_secret(&secret); + fscrypt_wipe_master_key_secret(&secret); return err; } @@ -794,7 +797,7 @@ int fscrypt_add_test_dummy_key(struct super_block *sb, fscrypt_get_test_dummy_secret(&secret); err = add_master_key(sb, &secret, key_spec); - wipe_master_key_secret(&secret); + fscrypt_wipe_master_key_secret(&secret); return err; } @@ -1017,6 +1020,12 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) mk = fscrypt_find_master_key(sb, &arg.key_spec); if (!mk) return -ENOKEY; + + if (fscrypt_fs_uses_extent_encryption(sb)) { + /* Keep going even if this has an error. */ + try_to_lock_encrypted_files(sb, mk); + } + down_write(&mk->mk_sem); /* If relevant, remove current user's (or all users) claim to the key */ @@ -1043,13 +1052,23 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) } } - /* No user claims remaining. Go ahead and wipe the secret. */ + /* No user claims remaining. */ err = -ENOKEY; - if (is_master_key_secret_present(&mk->mk_secret)) { - wipe_master_key_secret(&mk->mk_secret); + if (fscrypt_fs_uses_extent_encryption(sb) && refcount_read(&mk->mk_active_refs) > 1) { + mk->mk_soft_deleted = true; + err = 0; + } else if (is_master_key_secret_present(&mk->mk_secret)) { + fscrypt_wipe_master_key_secret(&mk->mk_secret); fscrypt_put_master_key_activeref(sb, mk); err = 0; + } else if (mk->mk_soft_deleted) { + /* + * Was soft deleted, but all inodes have stopped using it, and + * the secret was wiped by the last one. + */ + err = 0; } + inodes_remain = refcount_read(&mk->mk_active_refs) > 0; up_write(&mk->mk_sem); @@ -1149,7 +1168,7 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } down_read(&mk->mk_sem); - if (!is_master_key_secret_present(&mk->mk_secret)) { + if (mk->mk_soft_deleted || !is_master_key_secret_present(&mk->mk_secret)) { arg.status = refcount_read(&mk->mk_active_refs) > 0 ? FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED : FSCRYPT_KEY_STATUS_ABSENT /* raced with full removal */; diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 5e944ec4e36f..34d4df4acb19 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -570,6 +570,12 @@ static int find_and_lock_master_key(const struct fscrypt_common_info *cci, goto out_release_key; } + if (cci->ci_type != CI_EXTENT && mk->mk_soft_deleted) { + /* Only extent infos can use keys that have been soft deleted */ + err = -ENOKEY; + goto out_release_key; + } + *mk_ret = mk; return 0; @@ -598,6 +604,8 @@ static void remove_info_from_mk_decrypted_list(struct fscrypt_common_info *cci) { struct fscrypt_master_key *mk = cci->ci_master_key; if (mk) { + bool any_inodes; + /* * Remove this inode from the list of inodes that were unlocked * with the master key. In addition, if we're removing the last @@ -606,7 +614,28 @@ static void remove_info_from_mk_decrypted_list(struct fscrypt_common_info *cci) */ spin_lock(&mk->mk_decrypted_infos_lock); list_del(&cci->ci_master_key_link); + any_inodes = list_empty(&mk->mk_decrypted_infos); spin_unlock(&mk->mk_decrypted_infos_lock); + if (any_inodes) { + bool soft_deleted; + /* It might be that someone tried to remove this key, + * but there were still inodes open that could need new + * extents, which needed to be able to access the key + * secret. But now this was the last reference. So we + * can delete the key secret now. (We don't need to + * check for new inodes on the decrypted_inode list + * because once ->mk_soft_deleted is set, no new inode + * can join the list. + */ + down_write(&mk->mk_sem); + soft_deleted = mk->mk_soft_deleted; + if (soft_deleted) + fscrypt_wipe_master_key_secret(&mk->mk_secret); + up_write(&mk->mk_sem); + if (soft_deleted) + fscrypt_put_master_key_activeref(cci->ci_inode->i_sb, mk); + } + fscrypt_put_master_key_activeref(cci->ci_inode->i_sb, mk); } } @@ -933,6 +962,7 @@ EXPORT_SYMBOL(fscrypt_free_inode); int fscrypt_drop_inode(struct inode *inode) { const struct fscrypt_info *ci = fscrypt_get_info(inode); + const struct fscrypt_common_info *cci = &ci->info; /* * If ci is NULL, then the inode doesn't have an encryption key set up @@ -940,7 +970,7 @@ int fscrypt_drop_inode(struct inode *inode) * was provided via the legacy mechanism of the process-subscribed * keyrings, so we don't know whether it's been removed or not. */ - if (!ci || !ci->info.ci_master_key) + if (!ci || !cci->ci_master_key) return 0; /* @@ -960,7 +990,8 @@ int fscrypt_drop_inode(struct inode *inode) * then the thread removing the key will either evict the inode itself * or will correctly detect that it wasn't evicted due to the race. */ - return !is_master_key_secret_present(&ci->info.ci_master_key->mk_secret); + return cci->ci_master_key->mk_soft_deleted || + !is_master_key_secret_present(&cci->ci_master_key->mk_secret); } EXPORT_SYMBOL_GPL(fscrypt_drop_inode); From patchwork Sat Sep 2 05:54:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373028 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B060FCA0FF8 for ; Sat, 2 Sep 2023 05:56:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351631AbjIBF4N (ORCPT ); Sat, 2 Sep 2023 01:56:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236195AbjIBF4M (ORCPT ); Sat, 2 Sep 2023 01:56:12 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9038F10F4; Fri, 1 Sep 2023 22:56:09 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id BC5C5803B3; Sat, 2 Sep 2023 01:56:08 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634169; bh=4enh47oqQtM1rvRngoxnIU28aYTtdxzrlpSxtwxTslU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N4rbM3c56HkL7S2O0KF2bSJzJsctRFU/Gw19CvWZgm63Iahwa+ddzKIBBKCzBW6a8 aHaBod9P21LhDqZlyRroI8woDMdiuXoS84Y0Ho5jdjmG88rNxi3+x5LrzoyOm19qg6 egCqdidV1sr2HRUdvwz4SqixdhidAzwf2QHQ0D4TR1rmQAUbbV5K9IAAnHONYEjuEA csakQxBm6RuuwD6VkZU+cQhXVga/sNhfunsvpK40anxJomzmX293MlUcd+nd4ouwiS t8UGa4FP4WvU1LPx0CozMMFN8FiKtnE24jfTpJt/vbq9zplqF6Sno9ng+n+DiRQwAw fiWqTLQYw7gWg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 10/13] fscrypt: allow multiple extents to reference one info Date: Sat, 2 Sep 2023 01:54:28 -0400 Message-ID: <1a4861d291e08233a0f16b482af562d4fcb2caf1.1693630890.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org btrfs occasionally splits in-memory extents while holding a mutex. This means we can't just copy the info, since setting up a new inlinecrypt key requires taking a semaphore. Thus adding a mechanism to split extents and merely take a new reference on the info is necessary. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/fscrypt_private.h | 5 +++++ fs/crypto/keysetup.c | 19 ++++++++++++++++++- include/linux/fscrypt.h | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index dd7740105264..cf1eb7fe546f 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -307,6 +307,11 @@ struct fscrypt_info { */ struct fscrypt_extent_info { struct fscrypt_common_info info; + + /* Reference count. Normally 1, unless a extent info is shared by + * several virtual extents. + */ + refcount_t refs; }; typedef enum { diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 34d4df4acb19..f0f70b888bd8 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -919,6 +919,21 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode); +/** + * fscrypt_get_extent_info_ref() - mark a second extent using the same info + * @info: the info to be used by another extent + * + * Sometimes, an existing extent must be split into multiple extents in memory. + * In such a case, this function allows multiple extents to use the same extent + * info without allocating or taking any lock, which is necessary in certain IO + * paths. + */ +void fscrypt_get_extent_info_ref(struct fscrypt_extent_info *info) +{ + if (info) + refcount_inc(&info->refs); +} + /** * fscrypt_put_encryption_info() - free most of an inode's fscrypt data * @inode: an inode being evicted @@ -997,7 +1012,7 @@ EXPORT_SYMBOL_GPL(fscrypt_drop_inode); static void put_crypt_extent_info(struct fscrypt_extent_info *ci) { - if (!ci) + if (!ci || !refcount_dec_and_test(&ci->refs)) return; free_prepared_key(&ci->info); @@ -1042,6 +1057,8 @@ fscrypt_setup_extent_info(struct inode *inode, if (res) goto out; + refcount_set(&crypt_extent_info->refs, 1); + *info_ptr = crypt_extent_info; add_info_to_mk_decrypted_list(crypt_info, mk); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index b57fc5645076..577f9e0a6e97 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -362,7 +362,7 @@ int fscrypt_prepare_new_extent(struct inode *inode, void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr); int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len, struct fscrypt_extent_info **info_ptr); - +void fscrypt_get_extent_info_ref(struct fscrypt_extent_info *info); /* fname.c */ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, From patchwork Sat Sep 2 05:54:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373029 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B1B62CA0FFE for ; Sat, 2 Sep 2023 05:56:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351634AbjIBF4P (ORCPT ); Sat, 2 Sep 2023 01:56:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41896 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229566AbjIBF4O (ORCPT ); Sat, 2 Sep 2023 01:56:14 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD0E510F4; Fri, 1 Sep 2023 22:56:11 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 071B7803B8; Sat, 2 Sep 2023 01:56:10 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634171; bh=9CCHaKZC9yydBhLDgrT5+iVaEc3tpYmiIVZSh+piTxU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jCV0T54qQx7jVei/inQrTPBC0JEmuTOYXk6t0BBtP3xXzwirjcijNszvQb0fV9fjr goxXOmO4OwThCW+Xy3oFdEaRokE5Dat++6N+DiIgn4vMg4zyz2UBwNcRdt6H8FESTS xSR0MRxWHMsBEGDJLGI9PUzfCqGmAV2ZqH/gQ75mcWP0AEI6WSN8/8kJYLu0GfR/8G BtBjh340dPJGv0m/4q4TdDhrzywLZV9iu5p19f6K0lfYKIZp8dXTJ1le19InVVKkaf WbcgLu1cIrptKsNqQqcCgHBD6YLf5WnGQcUkQcyyf2Q1Sxd32cshhVGjRDAxevTWLY GL9RPm8fYwPOA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 11/13] fscrypt: cache list of inlinecrypt devices Date: Sat, 2 Sep 2023 01:54:29 -0400 Message-ID: <26e6ff03299df2a5c8f3d8727f36bfe8f15b686d.1693630890.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org btrfs sometimes frees extents while holding a mutex, which makes it impossible to free an inlinecrypt prepared key since that requires taking a semaphore. Therefore, we will need to offload prepared key freeing into an asynchronous process (rcu is insufficient since that can run in softirq context which is also incompatible with taking a semaphore). In order to avoid use-after-free on the filesystem superblock for keys being freed during shutdown, we need to cache the list of devices that the key has been loaded into, so that we can later remove it without reference to the superblock. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/fscrypt_private.h | 13 +++++++++++-- fs/crypto/inline_crypt.c | 20 +++++++++----------- fs/crypto/keysetup.c | 2 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cf1eb7fe546f..30459e219fc3 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -206,6 +206,16 @@ struct fscrypt_prepared_key { struct crypto_skcipher *tfm; #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT struct blk_crypto_key *blk_key; + + /* + * The list of devices that have this block key. + */ + struct block_device **devices; + + /* + * The number of devices in @ci_devices. + */ + size_t device_count; #endif enum fscrypt_prepared_key_type type; }; @@ -472,8 +482,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, const struct fscrypt_common_info *ci); -void fscrypt_destroy_inline_crypt_key(struct super_block *sb, - struct fscrypt_prepared_key *prep_key); +void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key); /* * Check whether the crypto transform or blk-crypto key has been allocated in diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index f0229234249c..19ebdef8508b 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -185,12 +185,15 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, if (err) break; } - kfree(devs); + if (err) { fscrypt_err(inode, "error %d starting to use blk-crypto", err); goto fail; } + prep_key->devices = devs; + prep_key->device_count = num_devs; + /* * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). * I.e., here we publish ->blk_key with a RELEASE barrier so that @@ -205,24 +208,19 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, return err; } -void fscrypt_destroy_inline_crypt_key(struct super_block *sb, - struct fscrypt_prepared_key *prep_key) +void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) { struct blk_crypto_key *blk_key = prep_key->blk_key; - struct block_device **devs; - unsigned int num_devs; unsigned int i; if (!blk_key) return; /* Evict the key from all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (!IS_ERR(devs)) { - for (i = 0; i < num_devs; i++) - blk_crypto_evict_key(devs[i], blk_key); - kfree(devs); - } + for (i = 0; i < prep_key->device_count; i++) + blk_crypto_evict_key(prep_key->devices[i], blk_key); + + kfree(prep_key->devices); kfree_sensitive(blk_key); } diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index f0f70b888bd8..4ea9b68363d5 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -184,7 +184,7 @@ void fscrypt_destroy_prepared_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) { crypto_free_skcipher(prep_key->tfm); - fscrypt_destroy_inline_crypt_key(sb, prep_key); + fscrypt_destroy_inline_crypt_key(prep_key); memzero_explicit(prep_key, sizeof(*prep_key)); } From patchwork Sat Sep 2 05:54:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373030 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF70CCA0FFC for ; Sat, 2 Sep 2023 05:56:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243397AbjIBF4R (ORCPT ); Sat, 2 Sep 2023 01:56:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41912 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233537AbjIBF4Q (ORCPT ); Sat, 2 Sep 2023 01:56:16 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D15EE10F4; Fri, 1 Sep 2023 22:56:13 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 3D1E6807EE; Sat, 2 Sep 2023 01:56:13 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634173; bh=f/KDTmj8qltktWsMIyHJJg5ruFMNDD45n9oJKTrCgso=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dS9LvgxPoY4UoJ//ZM/9dXk1DSqD1+1Sl9REVKkz69k82yR2d9zECadZsRdvW4Zts T6oytW2ogIfQCiTkhRKtA9EcwuUlkBDc08X2ZQD/v0t/s3DJ+Ur13iTFsBVD+Kfmbd bdBLoJ8bn0ezMnE3h/TCJFWMgeNJimH7AXIAQTEd3nBRFcHfOqiGffMRwql29Fu5TB kaxCdsPF7t/4WSqDWH3odgGmFtu4cxe3MOmV7xMHtUK3/VaKbWStjf9IxKb9YemyB5 Guvp83jXtrLan9zxbpxAXlQYIwrKnQNi8sfLw3v4auAAOfiJvhT3V72vzygHyGqc1+ jBPr/heMMA39A== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 12/13] fscrypt: allow asynchronous info freeing Date: Sat, 2 Sep 2023 01:54:30 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org btrfs sometimes frees extents while holding a mutex. This makes it hard to free the prepared keys associated therewith, as the free process may need to take a semaphore. Just offloading freeing to rcu doesn't work, as rcu may call the callback in softirq context, which also doesn't allow taking a semaphore. Thus, for extent infos, offload their freeing to the general system workqueue. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/fscrypt_private.h | 12 +++++++++--- fs/crypto/keyring.c | 6 +++--- fs/crypto/keysetup.c | 31 +++++++++++++++++++++++++++---- fs/crypto/keysetup_v1.c | 3 +-- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 30459e219fc3..67f33ad704a3 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -217,6 +217,12 @@ struct fscrypt_prepared_key { */ size_t device_count; #endif + /* + * For destroying asynchronously. + */ + struct work_struct work; + /* A pointer to free after destroy. */ + void *ptr_to_free; enum fscrypt_prepared_key_type type; }; @@ -528,8 +534,7 @@ fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, } static inline void -fscrypt_destroy_inline_crypt_key(struct super_block *sb, - struct fscrypt_prepared_key *prep_key) +fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) { } @@ -751,7 +756,8 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, const struct fscrypt_common_info *ci); void fscrypt_destroy_prepared_key(struct super_block *sb, - struct fscrypt_prepared_key *prep_key); + struct fscrypt_prepared_key *prep_key, + void *ptr_to_free); int fscrypt_set_per_info_enc_key(struct fscrypt_common_info *ci, const u8 *raw_key); diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 9235a5a9bcba..d4ec4ff27266 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -107,11 +107,11 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { fscrypt_destroy_prepared_key( - sb, &mk->mk_direct_keys[i]); + sb, &mk->mk_direct_keys[i], NULL); fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_64_keys[i]); + sb, &mk->mk_iv_ino_lblk_64_keys[i], NULL); fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_32_keys[i]); + sb, &mk->mk_iv_ino_lblk_32_keys[i], NULL); } memzero_explicit(&mk->mk_ino_hash_key, sizeof(mk->mk_ino_hash_key)); diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 4ea9b68363d5..293a7d765ca7 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -179,13 +179,36 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, return 0; } -/* Destroy a crypto transform object and/or blk-crypto key. */ -void fscrypt_destroy_prepared_key(struct super_block *sb, - struct fscrypt_prepared_key *prep_key) +static void __destroy_key(struct fscrypt_prepared_key *prep_key) { + void *ptr_to_free = prep_key->ptr_to_free; + crypto_free_skcipher(prep_key->tfm); fscrypt_destroy_inline_crypt_key(prep_key); memzero_explicit(prep_key, sizeof(*prep_key)); + if (ptr_to_free) + kfree_sensitive(ptr_to_free); +} + +static void __destroy_key_work(struct work_struct *work) +{ + struct fscrypt_prepared_key *prep_key = + container_of(work, struct fscrypt_prepared_key, work); + + __destroy_key(prep_key); +} + +/* Destroy a crypto transform object and/or blk-crypto key. */ +void fscrypt_destroy_prepared_key(struct super_block *sb, + struct fscrypt_prepared_key *prep_key, + void *ptr_to_free) +{ + prep_key->ptr_to_free = ptr_to_free; + if (fscrypt_fs_uses_extent_encryption(sb)) { + INIT_WORK(&prep_key->work, __destroy_key_work); + queue_work(system_unbound_wq, &prep_key->work); + } else + __destroy_key(prep_key); } /* Given a per-info encryption key, set up the info's crypto transform object */ @@ -594,8 +617,8 @@ static void free_prepared_key(struct fscrypt_common_info *cci) fscrypt_put_direct_key(cci->ci_enc_key); if (type == FSCRYPT_KEY_PER_INFO) { fscrypt_destroy_prepared_key(cci->ci_inode->i_sb, + cci->ci_enc_key, cci->ci_enc_key); - kfree_sensitive(cci->ci_enc_key); } } } diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index b57ed49ac201..8ccd8d4154d0 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -155,8 +155,7 @@ struct fscrypt_direct_key { static void free_direct_key(struct fscrypt_direct_key *dk) { if (dk) { - fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key); - kfree_sensitive(dk); + fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key, dk); } } From patchwork Sat Sep 2 05:54:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13373031 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7A662CA0FF6 for ; Sat, 2 Sep 2023 05:56:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243712AbjIBF4V (ORCPT ); Sat, 2 Sep 2023 01:56:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41950 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229566AbjIBF4U (ORCPT ); Sat, 2 Sep 2023 01:56:20 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5DEAC10F6; Fri, 1 Sep 2023 22:56:17 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id EF1FE803B8; Sat, 2 Sep 2023 01:56:15 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1693634177; bh=vGSsk/5B1Ps74F/JCs2e0jmq8/pZ+D4gbano0VHmuZQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CsQJqefQo4S0FeckXrm/Fx8CSd5O3C4qAb2hfo3ba3u7o9g0quJPjgN45Zmo9Pvr9 a0pK6k0QFgfe2JWUqFuhpGh70rwWMauBVzE47LyY1JxPE+VgW6Xx70QidgQCcBWSAo K7GuL7P9x9pOFOByyiEpe0lHaGJKzt5AbUriaSttXit1prahkWVF2yhc3c2sDYaXxn OPW7+v6WYbJDAIaqY3TOFOfloTlbGyq2v+UPpORWAR/jskrMVUyRnheiw/s51dfpFV m2NJxuE9MuWKDmH6xhOcGj2GlizkDIkYe14Aj7wECB7v5RjVoZuezz//9eZcqF+Vu7 xpceHqbqq1Waw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, ebiggers@kernel.org, ngompa13@gmail.com Cc: Sweet Tea Dorminy Subject: [RFC PATCH 13/13] fscrypt: update documentation for per-extent keys Date: Sat, 2 Sep 2023 01:54:31 -0400 Message-ID: <3024fee53a842fe8f09e07fe98e0df7b5e05e048.1693630890.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Add some documentation of how extent-based encryption works, hopefully enough for future filesystem users. Signed-off-by: Sweet Tea Dorminy --- Documentation/filesystems/fscrypt.rst | 43 +++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index eccd327e6df5..e862d59bd5b5 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -31,7 +31,7 @@ However, except for filenames, fscrypt does not encrypt filesystem metadata. Unlike eCryptfs, which is a stacked filesystem, fscrypt is integrated -directly into supported filesystems --- currently ext4, F2FS, and +directly into supported filesystems --- currently btrfs, ext4, F2FS, and UBIFS. This allows encrypted files to be read and written without caching both the decrypted and encrypted pages in the pagecache, thereby nearly halving the memory used and bringing it in line with @@ -125,6 +125,11 @@ However, these ioctls have some limitations: well as kill any processes whose working directory is in an affected encrypted directory. +- If the filesystem is using extent-based encryption, the master + encryption key will *not* be wiped from kernel memory until all + inodes using the key have been evicted (requiring that all files + using the key are closed). + - The kernel cannot magically wipe copies of the master key(s) that userspace might have as well. Therefore, userspace must wipe all copies of the master key(s) it makes as well; normally this should @@ -280,6 +285,11 @@ included in the IV. Moreover: key derived using the KDF. Users may use the same master key for other v2 encryption policies. +For filesystems with extent-based content encryption (e.g. btrfs), +this is the only choice. Data shared among multiple inodes must share +the exact same key, therefore necessitating inodes using the same key +for contents encryption. + IV_INO_LBLK_64 policies ----------------------- @@ -381,12 +391,13 @@ to individual filesystems. However, authenticated encryption (AE) modes are not currently supported because of the difficulty of dealing with ciphertext expansion. -Contents encryption -------------------- +Inode-based contents encryption +------------------------------- -For file contents, each filesystem block is encrypted independently. -Starting from Linux kernel 5.5, encryption of filesystems with block -size less than system's page size is supported. +Most filesystems use the previously discussed per-file keys. For these +filesystems, for file contents, each filesystem block is encrypted +independently. Starting from Linux kernel 5.5, encryption of filesystems +with block size less than system's page size is supported. Each block's IV is set to the logical block number within the file as a little endian number, except that: @@ -410,6 +421,26 @@ Note that because file logical block numbers are included in the IVs, filesystems must enforce that blocks are never shifted around within encrypted files, e.g. via "collapse range" or "insert range". +Extent-based contents encryption +-------------------------------- + +For certain filesystems (currently only btrfs), data is encrypted on a +per-extent basis, for whatever the filesystem's notion of an extent is. The +scheme is exactly as with inode-based contents encryption, except that the +'inode number' for an extent is requested from the filesystem instead of from +the file's inode, and the 'logical block number' refers to an offset within the +extent. + +Because the encryption material is per-extent instead of per-inode, as long +as the extent's encryption context does not change, the filesystem may shift +around the position of the extent, and may have multiple files referring to +the same encrypted extent. + +Not all extents within a file are decrypted simultaneously, so it is possible +for a file read to fail partway through the file if it crosses into an extent +whose key is unavailable. However, all writes will succeed, unless the key is +removed mid-write. + Filenames encryption --------------------