From patchwork Thu May 14 09:24:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Thumshirn X-Patchwork-Id: 11548381 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 81721139A for ; Thu, 14 May 2020 09:24:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6A6B92065F for ; Thu, 14 May 2020 09:24:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1589448284; bh=zvXseeiWqZhbyRu+de4m0Vi3e90rPE+Pd/20fj3nDSQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=ZK9nSOfdoACf0czor9onqFbDiLjpwUnQzCNtvXmUsWrabOQOCFcBJDsNuQFKSVPXW PHlKJYQaLsRVFLv9ltSiG2DTtfZwo3fHoomRHjKWdJXBqWqtUMKyR7s+v6aSR5FDAj vvhqgAb6h8OLDqk3DyzQJ4yQOxUAwPdy7Fr/iRaY= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726143AbgENJYn (ORCPT ); Thu, 14 May 2020 05:24:43 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:52622 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725925AbgENJYm (ORCPT ); Thu, 14 May 2020 05:24:42 -0400 Received: by mail-wm1-f66.google.com with SMTP id m24so19825183wml.2; Thu, 14 May 2020 02:24:41 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WeYIuSZz1h4OodCnVuDHUt8ckF0iXfCrXxOaLt9JUQ4=; b=ifQagYT0KZ8DlXoX0/FKQBtz62IxhxwoN/j0yatm2amT735vSHtKWq0xJOXUE0ehqC F3EyaJDOH0chBdvTBSj+c/lYGMTwb/vRMptZPpvds6k5W08YPSxXS7wzocIfIz+kOJqw idFOp3FxrAE8i6MvLedgXhHLfeTeDk62T+m0gtk3ZnHRo7VwohtcTHWUP7uHxJlaGCg7 WBf3URkM2mas1NRkaP9bFiy7PqYHQLbAf0wJkeo1wUFj+ADppkv/Mt93nW9C0AoynBjN 6d690IWwGNt+6iRTXB/Co1UOvLiP68EEb94qlyTC9wWxUaOOyx2OmqXaBS6tlAEVNV9d 4AZA== X-Gm-Message-State: AGi0PuYjPmBjqJw/QKPr3518fX+Bw0jqG/nMa/v+FLKJxraAakOhZx2s 8ADd5cuJm7cKP97/2L2tPRA= X-Google-Smtp-Source: APiQypKo/hTuashElmeVUqkdUJ1iJcJVYyopfByz7uBTm2Qvs4U5IP1bFV2zn1aHSauh82kQVVoPeQ== X-Received: by 2002:a1c:9948:: with SMTP id b69mr45735656wme.44.1589448280615; Thu, 14 May 2020 02:24:40 -0700 (PDT) Received: from linux-t19r.fritz.box (ppp-46-244-223-154.dynamic.mnet-online.de. [46.244.223.154]) by smtp.gmail.com with ESMTPSA id z132sm38877763wmc.29.2020.05.14.02.24.39 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 14 May 2020 02:24:40 -0700 (PDT) From: Johannes Thumshirn To: David Sterba Cc: linux-fsdevel@vger.kernel.org, linux-btrfs@vger.kernel.org, Eric Biggers , Richard Weinberger , Johannes Thumshirn , Johannes Thumshirn Subject: [PATCH v3 1/3] btrfs: rename btrfs_parse_device_options back to btrfs_parse_early_options Date: Thu, 14 May 2020 11:24:13 +0200 Message-Id: <20200514092415.5389-2-jth@kernel.org> X-Mailer: git-send-email 2.26.1 In-Reply-To: <20200514092415.5389-1-jth@kernel.org> References: <20200514092415.5389-1-jth@kernel.org> MIME-Version: 1.0 Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org From: Johannes Thumshirn As btrfs_parse_device_options() now doesn't only parse the -o device mount option but -o auth_key as well, it makes sense to rename it back to btrfs_parse_early_options(). This reverts commit fa59f27c8c35bbe00af8eff23de446a7f4b048b0. Signed-off-by: Johannes Thumshirn --- fs/btrfs/super.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 438ecba26557..07cec0d16348 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -482,8 +482,9 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, case Opt_subvolrootid: case Opt_device: /* - * These are parsed by btrfs_parse_subvol_options or - * btrfs_parse_device_options and can be ignored here. + * These are parsed by btrfs_parse_subvol_options + * and btrfs_parse_early_options + * and can be happily ignored here. */ break; case Opt_nodatasum: @@ -919,8 +920,9 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, * All other options will be parsed on much later in the mount process and * only when we need to allocate a new super block. */ -static int btrfs_parse_device_options(const char *options, fmode_t flags, - void *holder) +static int btrfs_parse_early_options(struct btrfs_fs_info *info, + const char *options, fmode_t flags, + void *holder) { substring_t args[MAX_OPT_ARGS]; char *device_name, *opts, *orig, *p; @@ -987,7 +989,7 @@ static int btrfs_parse_subvol_options(const char *options, char **subvol_name, /* * strsep changes the string, duplicate it because - * btrfs_parse_device_options gets called later + * btrfs_parse_early_options gets called later */ opts = kstrdup(options, GFP_KERNEL); if (!opts) @@ -1551,7 +1553,7 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type, } mutex_lock(&uuid_mutex); - error = btrfs_parse_device_options(data, mode, fs_type); + error = btrfs_parse_early_options(fs_info, data, mode, fs_type); if (error) { mutex_unlock(&uuid_mutex); goto error_fs_info; From patchwork Thu May 14 09:24:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Thumshirn X-Patchwork-Id: 11548389 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 25088912 for ; Thu, 14 May 2020 09:24:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 059082065D for ; Thu, 14 May 2020 09:24:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1589448289; bh=LoDGUEKn1aEO8pSLAZdDJuODuOXK0fLXXuH1HDJNmJE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=VUA4ROR6/40OFXmNYIigo1oqIDW4g7OG4cKRoKEr/PR8ChQHARrhSGaghY/etM9Ds SjRgJvpDhdbuokc9LGSnCJZr6EowGqoFs9B2Vkl4TJa2zuaCmgOB/1ge94IBxiLfxx JUvOQzrIMBosWUjUABTs1Yx9MyP0oWcVokzakImM= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726176AbgENJYr (ORCPT ); Thu, 14 May 2020 05:24:47 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:53706 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725925AbgENJYq (ORCPT ); Thu, 14 May 2020 05:24:46 -0400 Received: by mail-wm1-f66.google.com with SMTP id k12so29370563wmj.3; Thu, 14 May 2020 02:24:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lt+FtTKfoUfoObL8aO8qizMpOeHO6DXwfi/WUhXbZpo=; b=TDE/+mc//3huoZxErwxMkPG4JL7z1CCINXwhqFrG40D1DMIIagu6YD6/HtWATjOCYh fTwh/zKnCfYeVbMh17jV3xogYBcj5SQfngCa7mLdHD9TLuEq0xsx9Gz5u8ID/n+LaqbQ mOs+H6TzpacHI/hunZN0cO2j7PB1RUy3yL5frlPB//98kx3gifNTHBMTMTwoPe/PbPf2 GlcYNEElf+VeexiERzngeDgML+5YL3ig1RfUX169SCAED6K0h45+VupFSf6qfdVNxMF+ +NVmz5qCcjEXo4ggwsrTrnndJ9WMiL97bl+J2gnaRtCIhrOkax/ltNcGN2spDmqhvO1F NsXQ== X-Gm-Message-State: AOAM532womOcd8EJPsqT6002I0gNVkrxLUzh1PGDcfqWFQalXWYhmZF5 SGU4F4YOReHXx1Ecsra1nc4= X-Google-Smtp-Source: ABdhPJwj4zsE+WHyqOjCTKqoQw+cfolRDPRW8TwjDcUNm+ZmFYAL4HppbVk3eqAXPyDjhioFlz7HVA== X-Received: by 2002:a1c:31d6:: with SMTP id x205mr14521619wmx.105.1589448281664; Thu, 14 May 2020 02:24:41 -0700 (PDT) Received: from linux-t19r.fritz.box (ppp-46-244-223-154.dynamic.mnet-online.de. [46.244.223.154]) by smtp.gmail.com with ESMTPSA id z132sm38877763wmc.29.2020.05.14.02.24.40 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 14 May 2020 02:24:41 -0700 (PDT) From: Johannes Thumshirn To: David Sterba Cc: linux-fsdevel@vger.kernel.org, linux-btrfs@vger.kernel.org, Eric Biggers , Richard Weinberger , Johannes Thumshirn , Johannes Thumshirn Subject: [PATCH v3 2/3] btrfs: add authentication support Date: Thu, 14 May 2020 11:24:14 +0200 Message-Id: <20200514092415.5389-3-jth@kernel.org> X-Mailer: git-send-email 2.26.1 In-Reply-To: <20200514092415.5389-1-jth@kernel.org> References: <20200514092415.5389-1-jth@kernel.org> MIME-Version: 1.0 Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org From: Johannes Thumshirn Add authentication support for a BTRFS file-system. This works, because in BTRFS every meta-data block as well as every data-block has a own checksum. For meta-data the checksum is in the meta-data node itself. For data blocks, the checksums are stored in the checksum tree. When replacing the checksum algorithm with a keyed hash, like HMAC(SHA256), a key is needed to mount a verified file-system. This key also needs to be used at file-system creation time. We have to used a keyed hash scheme, in contrast to doing a normal cryptographic hash, to guarantee integrity of the file system, as a potential attacker could just generate the corresponding cryptographic hash for forged file-system operations and the changes would go unnoticed. Having a keyed hash only on the topmost Node of a tree or even just in the super-block and using cryptographic hashes on the normal meta-data nodes and checksum tree entries doesn't work either, as the BTRFS B-Tree's Nodes do not include the checksums of their respective child nodes, but only the block pointers and offsets where to find them on disk. Also note, we do not need a incompat R/O flag for this, because if an old kernel tries to mount an authenticated file-system it will fail the initial checksum type verification and thus refuses to mount. The key has to be supplied by the kernel's keyring and the method of getting the key securely into the kernel is not subject of this patch. Example usage: Create a file-system with authentication key 0123456 mkfs.btrfs --csum "hmac(sha256)" --auth-key 0123456 /dev/disk Add the key to the kernel's keyring as keyid 'btrfs:foo' keyctl add logon btrfs:foo 0123456 @u Mount the fs using the 'btrfs:foo' key mount -o auth_key=btrfs:foo,auth_hash_name="hmac(sha256)" /dev/disk /mnt/point Signed-off-by: Johannes Thumshirn --- fs/btrfs/Kconfig | 2 + fs/btrfs/ctree.c | 22 +++++++++- fs/btrfs/ctree.h | 5 ++- fs/btrfs/disk-io.c | 71 +++++++++++++++++++++++++++++++-- fs/btrfs/ioctl.c | 7 +++- fs/btrfs/super.c | 51 ++++++++++++++++++++++- include/uapi/linux/btrfs_tree.h | 1 + 7 files changed, 150 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 575636f6491e..b7e9ce25b622 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -8,6 +8,8 @@ config BTRFS_FS select CRYPTO_XXHASH select CRYPTO_SHA256 select CRYPTO_BLAKE2B + select CRYPTO_HMAC + select KEYS select ZLIB_INFLATE select ZLIB_DEFLATE select LZO_COMPRESS diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 746dec22f250..2907bd054dd6 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -31,7 +31,7 @@ static void del_ptr(struct btrfs_root *root, struct btrfs_path *path, static const struct btrfs_csums { u16 size; - const char name[10]; + const char name[12]; const char driver[12]; } btrfs_csums[] = { [BTRFS_CSUM_TYPE_CRC32] = { .size = 4, .name = "crc32c" }, @@ -39,6 +39,7 @@ static const struct btrfs_csums { [BTRFS_CSUM_TYPE_SHA256] = { .size = 32, .name = "sha256" }, [BTRFS_CSUM_TYPE_BLAKE2] = { .size = 32, .name = "blake2b", .driver = "blake2b-256" }, + [BTRFS_CSUM_TYPE_HMAC_SHA256] = { .size = 32, .name = "hmac(sha256)" } }; int btrfs_super_csum_size(const struct btrfs_super_block *s) @@ -56,12 +57,29 @@ const char *btrfs_super_csum_name(u16 csum_type) return btrfs_csums[csum_type].name; } +static const char *btrfs_auth_csum_driver(struct btrfs_fs_info *fs_info) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(btrfs_csums); i++) { + if (!strncmp(fs_info->auth_hash_name, btrfs_csums[i].name, + strlen(btrfs_csums[i].name))) + return btrfs_csums[i].driver[0] ? + btrfs_csums[i].driver : btrfs_csums[i].name; + } + + return NULL; +} + /* * Return driver name if defined, otherwise the name that's also a valid driver * name */ -const char *btrfs_super_csum_driver(u16 csum_type) +const char *btrfs_super_csum_driver(struct btrfs_fs_info *info, u16 csum_type) { + if (btrfs_test_opt(info, AUTH_KEY)) + return btrfs_auth_csum_driver(info); + /* csum type is validated at mount time */ return btrfs_csums[csum_type].driver[0] ? btrfs_csums[csum_type].driver : diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0a1fa1526c43..b9e4cf5b9fd3 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -931,6 +931,8 @@ struct btrfs_fs_info { struct rb_root swapfile_pins; struct crypto_shash *csum_shash; + char *auth_key_name; + char *auth_hash_name; /* * Number of send operations in progress. @@ -1239,6 +1241,7 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info) #define BTRFS_MOUNT_NOLOGREPLAY (1 << 27) #define BTRFS_MOUNT_REF_VERIFY (1 << 28) #define BTRFS_MOUNT_DISCARD_ASYNC (1 << 29) +#define BTRFS_MOUNT_AUTH_KEY (1 << 30) #define BTRFS_DEFAULT_COMMIT_INTERVAL (30) #define BTRFS_DEFAULT_MAX_INLINE (2048) @@ -2186,7 +2189,7 @@ BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block, int btrfs_super_csum_size(const struct btrfs_super_block *s); const char *btrfs_super_csum_name(u16 csum_type); -const char *btrfs_super_csum_driver(u16 csum_type); +const char *btrfs_super_csum_driver(struct btrfs_fs_info *info, u16 csum_type); size_t __const btrfs_get_num_csums(void); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 714b57553ed6..6ed5d1191cdf 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include "ctree.h" @@ -100,8 +101,10 @@ void __cold btrfs_end_io_wq_exit(void) static void btrfs_free_csum_hash(struct btrfs_fs_info *fs_info) { - if (fs_info->csum_shash) + if (fs_info->csum_shash) { crypto_free_shash(fs_info->csum_shash); + fs_info->csum_shash = NULL; + } } /* @@ -339,6 +342,7 @@ static bool btrfs_supported_super_csum(u16 csum_type) case BTRFS_CSUM_TYPE_XXHASH: case BTRFS_CSUM_TYPE_SHA256: case BTRFS_CSUM_TYPE_BLAKE2: + case BTRFS_CSUM_TYPE_HMAC_SHA256: return true; default: return false; @@ -1509,6 +1513,8 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info) percpu_counter_destroy(&fs_info->dio_bytes); percpu_counter_destroy(&fs_info->dev_replace.bio_counter); btrfs_free_csum_hash(fs_info); + kfree(fs_info->auth_key_name); + kfree(fs_info->auth_hash_name); btrfs_free_stripe_hash_table(fs_info); btrfs_free_ref_cache(fs_info); kfree(fs_info->balance_ctl); @@ -2178,10 +2184,16 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info, static int btrfs_init_csum_hash(struct btrfs_fs_info *fs_info, u16 csum_type) { struct crypto_shash *csum_shash; - const char *csum_driver = btrfs_super_csum_driver(csum_type); + const char *csum_driver; + struct key *key; + const struct user_key_payload *ukp; + int err = -EINVAL; - csum_shash = crypto_alloc_shash(csum_driver, 0, 0); + csum_driver = btrfs_super_csum_driver(fs_info, csum_type); + if (!csum_driver) + return err; + csum_shash = crypto_alloc_shash(csum_driver, 0, 0); if (IS_ERR(csum_shash)) { btrfs_err(fs_info, "error allocating %s hash for checksum", csum_driver); @@ -2190,7 +2202,57 @@ static int btrfs_init_csum_hash(struct btrfs_fs_info *fs_info, u16 csum_type) fs_info->csum_shash = csum_shash; - return 0; + /* + * If we're not doing authentication, we're done by now. If we use + * authentication and the auth_hash_name was bogus crypt_alloc_shash + * would have dropped out by now. Validation that both auth_hash_name + * and auth_key_name have been supplied is done in + * btrfs_parse_early_options(), so we should be good to go from here on + * and start authenticating the file-system. + */ + if (!btrfs_test_opt(fs_info, AUTH_KEY)) + return 0; + + if (strncmp(fs_info->auth_key_name, "btrfs:", 6)) { + btrfs_err(fs_info, + "authentication key must start with 'btrfs:'"); + goto out_free_hash; + } + + key = request_key(&key_type_logon, fs_info->auth_key_name, NULL); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto out_free_hash; + } + + down_read(&key->sem); + + ukp = user_key_payload_locked(key); + if (!ukp) { + btrfs_err(fs_info, "error getting payload for key %s", + fs_info->auth_key_name); + err = -EKEYREVOKED; + goto out; + } + + err = crypto_shash_setkey(fs_info->csum_shash, ukp->data, ukp->datalen); + if (err) + btrfs_err(fs_info, "error setting key %s for verification", + fs_info->auth_key_name); + +out: + if (err) { + btrfs_free_csum_hash(fs_info); + } + + up_read(&key->sem); + key_put(key); + + return err; + +out_free_hash: + btrfs_free_csum_hash(fs_info); + return err; } static int btrfs_replay_log(struct btrfs_fs_info *fs_info, @@ -3371,6 +3433,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device btrfs_stop_all_workers(fs_info); btrfs_free_block_groups(fs_info); fail_alloc: + btrfs_free_csum_hash(fs_info); btrfs_mapping_tree_free(&fs_info->mapping_tree); iput(fs_info->btree_inode); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 40b729dce91c..59bbb2c860d5 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -242,7 +242,12 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) else binode_flags &= ~BTRFS_INODE_DIRSYNC; if (fsflags & FS_NOCOW_FL) { - if (S_ISREG(inode->i_mode)) { + if (btrfs_test_opt(fs_info, AUTH_KEY)) { + btrfs_err(fs_info, + "Cannot set nodatacow or nodatasum on authenticated file-system"); + ret = -EPERM; + goto out_unlock; + } else if (S_ISREG(inode->i_mode)) { /* * It's safe to turn csums off here, no extents exist. * Otherwise we want the flag to reflect the real COW diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 07cec0d16348..88164b916c28 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -342,6 +342,8 @@ enum { Opt_treelog, Opt_notreelog, Opt_usebackuproot, Opt_user_subvol_rm_allowed, + Opt_auth_key, + Opt_auth_hash_name, /* Deprecated options */ Opt_alloc_start, @@ -410,6 +412,8 @@ static const match_table_t tokens = { {Opt_notreelog, "notreelog"}, {Opt_usebackuproot, "usebackuproot"}, {Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"}, + {Opt_auth_key, "auth_key=%s"}, + {Opt_auth_hash_name, "auth_hash_name=%s"}, /* Deprecated options */ {Opt_alloc_start, "alloc_start=%s"}, @@ -488,6 +492,11 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, */ break; case Opt_nodatasum: + if (btrfs_test_opt(info, AUTH_KEY)) { + btrfs_info(info, + "nodatasum not supported on an authnticated file-system"); + break; + } btrfs_set_and_info(info, NODATASUM, "setting nodatasum"); break; @@ -503,6 +512,11 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, btrfs_clear_opt(info->mount_opt, NODATASUM); break; case Opt_nodatacow: + if (btrfs_test_opt(info, AUTH_KEY)) { + btrfs_info(info, + "nodatacow not supported on an authnticated file-system"); + break; + } if (!btrfs_test_opt(info, NODATACOW)) { if (!btrfs_test_opt(info, COMPRESS) || !btrfs_test_opt(info, FORCE_COMPRESS)) { @@ -950,7 +964,8 @@ static int btrfs_parse_early_options(struct btrfs_fs_info *info, continue; token = match_token(p, tokens, args); - if (token == Opt_device) { + switch (token) { + case Opt_device: device_name = match_strdup(&args[0]); if (!device_name) { error = -ENOMEM; @@ -963,9 +978,40 @@ static int btrfs_parse_early_options(struct btrfs_fs_info *info, error = PTR_ERR(device); goto out; } + break; + case Opt_auth_key: + info->auth_key_name = match_strdup(&args[0]); + if (!info->auth_key_name) { + error = -ENOMEM; + goto out; + } + break; + case Opt_auth_hash_name: + info->auth_hash_name = match_strdup(&args[0]); + if (!info->auth_hash_name) { + error = -ENOMEM; + goto out; + } + break; + default: + break; } } + /* + * Check that both auth_key_name and auth_hash_name are either present + * or absent and error out if only one of them is set. + */ + if (info->auth_key_name && info->auth_hash_name) { + btrfs_info(info, "doing authentication"); + btrfs_set_opt(info->mount_opt, AUTH_KEY); + } else if ((info->auth_key_name && !info->auth_hash_name) || + (!info->auth_key_name && info->auth_hash_name)) { + btrfs_err(info, + "auth_key and auth_hash_name must be supplied together"); + error = -EINVAL; + } + out: kfree(orig); return error; @@ -1405,6 +1451,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) #endif if (btrfs_test_opt(info, REF_VERIFY)) seq_puts(seq, ",ref_verify"); + if (btrfs_test_opt(info, AUTH_KEY)) + seq_printf(seq, ",auth_key=%s", info->auth_key_name); seq_printf(seq, ",subvolid=%llu", BTRFS_I(d_inode(dentry))->root->root_key.objectid); seq_puts(seq, ",subvol="); @@ -2526,4 +2574,5 @@ MODULE_LICENSE("GPL"); MODULE_SOFTDEP("pre: crc32c"); MODULE_SOFTDEP("pre: xxhash64"); MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: hmac"); MODULE_SOFTDEP("pre: blake2b-256"); diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index a3f3975df0de..dfc22b995f60 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -305,6 +305,7 @@ enum btrfs_csum_type { BTRFS_CSUM_TYPE_XXHASH = 1, BTRFS_CSUM_TYPE_SHA256 = 2, BTRFS_CSUM_TYPE_BLAKE2 = 3, + BTRFS_CSUM_TYPE_HMAC_SHA256 = 4, }; /* From patchwork Thu May 14 09:24:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johannes Thumshirn X-Patchwork-Id: 11548385 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DCB33139A for ; Thu, 14 May 2020 09:24:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C1EAF20671 for ; Thu, 14 May 2020 09:24:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1589448287; bh=RLhFeG1Hcll8laf2vC30PEK8+IusVcBZ17eRIEflh3M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=fDjtlecoYimUguCVOZA3+2qQgFNZ7w5BouzA4iuDIskiI0hRoXI8TChdIGpcQQ/F9 jrRqS73uJQcx7gn6qsP8tbudqSJniU7hgqu0Z/scB8cYeEx1Cq7Rn+pq6IQd+h0m/M fN+IIUOzIaN/aMrqKvyvmct/tr+EaLBhgldKwMiE= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726168AbgENJYq (ORCPT ); Thu, 14 May 2020 05:24:46 -0400 Received: from mail-wr1-f68.google.com ([209.85.221.68]:39780 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726155AbgENJYq (ORCPT ); Thu, 14 May 2020 05:24:46 -0400 Received: by mail-wr1-f68.google.com with SMTP id l18so2932712wrn.6; Thu, 14 May 2020 02:24:43 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=80LUdNwveCgDKH5LIakdxC++ApFkWqVk9A6Kl2+ExLI=; b=XLKj//BiGoBvAORlsOAPwfYQda+VHJES9DfQLSnbyGzHi1AsvP3V9cJWG9Ug5uYjNL Xzxyq3WOWdE9vtPUyTSVvrmuGXBCJuAODn4raQMcQBJm4XO8kHkD2dFu6kFN+y4eyejx RqvN3ueLjyxMop9+9jsnXUHmfPQxVCUwoGSaRst4sOqhn5EwuG/29Y+krov2VdyUaAOE Y8F1eJNtOY2KHHWGhbQNrvWlFSdk6BfnkcvwyfUuB8rfxzWwWs/sQTgvzTuM1FwmGZhK r+IpCWovVVfEs/jKJ/eljD7U2WxeJXKZvB34k/BIQsapwpuLUUKfAKaXHu9CqdaYdhaK d9Rw== X-Gm-Message-State: AOAM532Jly47SKMImzbx2YXzsMioKz/4liLirnTsVKaR/WiXtMoAH+z6 dEYktD0T/YqnrQaT1fuducc= X-Google-Smtp-Source: ABdhPJzicvH+ra/nFQ9SaGCBu22VOQIX1mvpOZSNAj5K/U+GW3iZIBto+GEJ9ZZ1erCjMRiJJ52PJw== X-Received: by 2002:adf:dc81:: with SMTP id r1mr4798347wrj.0.1589448283010; Thu, 14 May 2020 02:24:43 -0700 (PDT) Received: from linux-t19r.fritz.box (ppp-46-244-223-154.dynamic.mnet-online.de. [46.244.223.154]) by smtp.gmail.com with ESMTPSA id z132sm38877763wmc.29.2020.05.14.02.24.41 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 14 May 2020 02:24:42 -0700 (PDT) From: Johannes Thumshirn To: David Sterba Cc: linux-fsdevel@vger.kernel.org, linux-btrfs@vger.kernel.org, Eric Biggers , Richard Weinberger , Johannes Thumshirn , Jonathan Corbet Subject: [PATCH v3 3/3] btrfs: document btrfs authentication Date: Thu, 14 May 2020 11:24:15 +0200 Message-Id: <20200514092415.5389-4-jth@kernel.org> X-Mailer: git-send-email 2.26.1 In-Reply-To: <20200514092415.5389-1-jth@kernel.org> References: <20200514092415.5389-1-jth@kernel.org> MIME-Version: 1.0 Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org From: Johannes Thumshirn Document the design, guarantees and limitations of an authenticated BTRFS file-system. Cc: Jonathan Corbet Signed-off-by: Johannes Thumshirn --- .../filesystems/btrfs-authentication.rst | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 Documentation/filesystems/btrfs-authentication.rst diff --git a/Documentation/filesystems/btrfs-authentication.rst b/Documentation/filesystems/btrfs-authentication.rst new file mode 100644 index 000000000000..f13cab248fc0 --- /dev/null +++ b/Documentation/filesystems/btrfs-authentication.rst @@ -0,0 +1,168 @@ +.. SPDX-License-Identifier: GPL-2.0 + +:orphan: + +.. BTRFS Authentication +.. Western Digital or it's affiliates +.. 2020 + +Introduction +============ + +This document describes an approach get file contents _and_ full meta-data +authentication for BTRFS. + +This is possible because BTRFS uses checksums embedded in its on-disk +meta-data structures as well as checksums for each individual extent of data. +The primary intent of these checksums was to detect bit-flips of data at rest. +But this mechanism can be extended to provide authentication of all on-disk +data when the checksum algorithm is replaced by a cryptographically secure +keyed hash. + +BTRFS Data Structures +--------------------- + +BTRFS utilizes a special copy-on-write b-tree to store all contents of the +file-system on disk. + +Meta-Data +~~~~~~~~~ + +On-disk meta-data in BTRFS is stored in copy-on-write b-trees. These b-trees +are build using two data structures, ``struct btrfs_node`` and ``struct +btrfs_leaf``. Both of these structures start with a ``struct btrfs_header``. +This structure has amongst other fields a checksum field, protecting it's +contents. As the checksum is the first entry in the structure, the whole +structure is protected by this checksum. The superblock (``struct +btrfs_super_block``) is the first on-disk structure which is read on mount and +it as well starts with a checksum field protecting the rest of the structure. +The super block is also needed to read the addresses of the other file system +b-trees, so their location on disk is protected by the checksum. + +:: + + BTRFS Header + +------+------+--------+-------+-----------------+----+-------+---------+ + | csum | fsid | bytenr | flags | chunk_tree_uuid | gen| owner | nritems | + +------+------+--------+-------+-----------------+----+-------+---------+ + BTRFS Node + +--------+-------------+-------------+-----+ + | Header | key pointer | key pointer | ... | + +--------+-------------+-------------+-----+ + BTRFS Leaf + +--------+------+------+-----+ + | Header | item | item | ... | + +--------+------+------+-----+ + + Figure 1: BTRFS Header, Node and Leaf data structures + +User-Data +~~~~~~~~~ + +User data in BRTFS is also protected by checksums, but this checksum is not +stored alongside the data, as it is with meta-data, but stored in a separate +b-tree, the checksum tree. The leafs of this tree store the checksums of the +user-data. The tree nodes and leafs are of ``struct btrfs_node`` or ``struct +btrfs_leaf``, so integrity of this tree is protected as well. + +BTRFS Authentication +==================== + +This chapter introduces BTRFS authentication which enables BTRFS to verify +the authenticity and integrity of metadata and file contents stored on disk. + +Threat Model +------------ + +BTRFS authentication enables detection of offline data modification. While it +does not prevent it, it enables (trusted) code to check the integrity and +authenticity of on-disk file contents and filesystem metadata. This covers +attacks where file contents are swapped. + +BTRFS authentication will not protect against a rollback of full disk +contents. Ie. an attacker can still dump the disk and restore it at a later +time without detection. It will also not protect against a rollback of one +transaction. That means an attacker is able to partially undo changes. This is +possible, because BTRFS does not immediately overwrite obsolete versions of +its meta-data but keeps older generations until they get garbage collected. + +BTRFS authentication does not cover attacks where an attacker is able to +execute code on the device after the authentication key was provided. +Additional measures like secure boot and trusted boot have to be taken to +ensure that only trusted code is executed on a device. + +As the file-system authentication key is also needed to update data structures +on disk, the key has to be in the kernel's keyring for the whole time the +file-system is mounted. An attacker that is able to compromise the kernel can +be able to extract the key from the kernel's keyring and thus can gain the +ability to modify the file-system later on. + +Authentication +-------------- + +To be able to fully trust data read from disk, all BTRFS data structures +stored on disk are authenticated. That is: + +- The super blocks +- The file-system b-trees +- The user-data + + +Super-block +~~~~~~~~~~~ + +In order to be able to authenticate the file-system's super-block, the +checksum stored in the checksum field at the beginning of ``struct +btrfs_super_block`` protecting its contents is replaced by a +cryptographically secure keyed hash. In order to generate a valid super-block +or to validate the super-block, one has to provide a key as an additional +input for the hash function. The super-block is the starting point to read all +on disk tree structures, so if we cannot trust the authenticity of the +super-block anymore, we cannot trust the whole file-system. + +B-Trees +~~~~~~~ + +Starting from the super-block's root-tree root, the root tree holds the b-tree +roots of all other on disk b-trees. All other file-system meta-data can be +derived from the trees stored in this tree. As all b-trees in BTRFS are built +using ``struct btrfs_node`` and ``struct btrfs_leaf`` each building block of +each tree is checksummed. These checksums are replaced with the cryptographically +secure keyed hash algorithm and the authentication key used to verify the +super-block in the mount phase. Without this key it is impossible to alter any +of the file-system structure without generating invalid hashes. + +User-data +~~~~~~~~~ + +The checksums for the user or file-data are stored in a separate b-tree, the +checksum tree. As this tree in itself is authenticated, only the data stored +in it needs to be authenticated. This is done by replacing the checksums +stored on disk by the cryptographically secure keyed hash algorithm used for +the super-block and other meta-data. So each written file block will get +checksummed with the authentication key and without supplying the correct key +it is impossible to write data on disk, which can be read back without +failing the authentication test. If this test is failed, an I/O error is +reported back to the user. + +Key Management +-------------- + +For simplicity, BTRFS authentication uses a single key to compute the keyed +hashes of the super-block, b-tree nodes and leafs as well as file-blocks. This +key has to be available on creation of the file-system (`mkfs.btrfs`) to +authenticate all b-tree elements and the super-blocks. Further, it has to be +available on mount of the file-system to verify the meta-data and user-data +stored in the file-system. + +Limitations +----------- + +As some optional features of BTRFS disable the generation of checksums, these +features are incompatible with an authenticated BTRFS. +These features are: +- nodatacow +- nodatasum + +As well as any offline modifications to the file-system, like setting an FS +label while the FS is unmounted.