diff mbox series

[v3,2/3] btrfs: add authentication support

Message ID 20200514092415.5389-3-jth@kernel.org (mailing list archive)
State New, archived
Headers show
Series Add file-system authentication to BTRFS | expand

Commit Message

Johannes Thumshirn May 14, 2020, 9:24 a.m. UTC
From: Johannes Thumshirn <johannes.thumshirn@wdc.com>

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 <jthumshirn@suse.de>

---
 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(-)

Comments

David Sterba May 27, 2020, 1:24 p.m. UTC | #1
On Thu, May 14, 2020 at 11:24:14AM +0200, Johannes Thumshirn wrote:
> From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
> 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

I tried to follow the example but the filesystem does not mount. But
what almost shocked me was the way the key is specified on the userspace
side.

$ mkfs.btrfs --csum "hmac(sha256)" --auth-key 0123456 /dev/disk

"0123456" are the raw bytes of the key? Seriously?

And how it's passed to the hmac code:

 gcry_mac_hd_t mac;
 gcry_mac_open(&mac, GCRY_MAC_HMAC_SHA256, 0, NULL);
 gcry_mac_setkey(mac, fs_info->auth_key, strlen(fs_info->auth_key));
 gcry_mac_write(mac, buf, length);
 gcry_mac_read(mac, out, &length);

Strlen means the key must avoid char 0 and I don't think we want do any
decoding from ascii-hex format, when there's the whole keyctl
infrastructure.

The key for all userspace commands needs to be specified the same way as
for kernel, ie. "--auth-key btrfs:foo" and use the appropriate ioctls to
read the key bytes.
Johannes Thumshirn May 27, 2020, 1:54 p.m. UTC | #2
On 27/05/2020 15:25, David Sterba wrote:
> On Thu, May 14, 2020 at 11:24:14AM +0200, Johannes Thumshirn wrote:
>> From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
>> 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
> 
> I tried to follow the example but the filesystem does not mount. But
> what almost shocked me was the way the key is specified on the userspace
> side.
> 
> $ mkfs.btrfs --csum "hmac(sha256)" --auth-key 0123456 /dev/disk
> 
> "0123456" are the raw bytes of the key? Seriously?
> 
> And how it's passed to the hmac code:
> 
>  gcry_mac_hd_t mac;
>  gcry_mac_open(&mac, GCRY_MAC_HMAC_SHA256, 0, NULL);
>  gcry_mac_setkey(mac, fs_info->auth_key, strlen(fs_info->auth_key));
>  gcry_mac_write(mac, buf, length);
>  gcry_mac_read(mac, out, &length);
> 
> Strlen means the key must avoid char 0 and I don't think we want do any
> decoding from ascii-hex format, when there's the whole keyctl
> infrastructure.
> 
> The key for all userspace commands needs to be specified the same way as
> for kernel, ie. "--auth-key btrfs:foo" and use the appropriate ioctls to
> read the key bytes.
> 

Hohum?

Here's what I just did:

rapido1:/# keyctl add logon btrfs:foo 0123456 @u
1020349071
rapido1:/# mkfs.btrfs --csum "hmac(sha256)" --auth-key 0123456 /dev/zram1
btrfs-progs v5.6
See http://btrfs.wiki.kernel.org for more information.

Detected a SSD, turning off metadata duplication.  Mkfs with -m dup if you want to force metadata duplication.
Label:              (null)
UUID:               56ae43ac-f333-4ed4-933a-356aed534115
[   31.005743] BTRFS: device fsid 56ae43ac-f333-4ed4-933a-356aed534115 devid 1 transid 5 /dev/zram1 scanned by mkfs.btrfs (241)

Sector size:        4096
Filesystem size:    3.00GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         single            8.00MiB
  System:           single            4.00MiB
SSD detected:       yes
Incompat features:  extref, skinny-metadata
Checksum:           hmac-sha256
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1     3.00GiB  /dev/zram1


rapido1:/# mount -o auth_key=btrfs:foo,auth_hash_name="hmac(sha256)" /dev/zram1 /mnt/       
[   65.959465] BTRFS info (device (efault)): doing authentication
[   65.963204] BTRFS info (device zram1): disk space caching is enabled
[   65.964137] BTRFS info (device zram1): has skinny extents
[   65.964912] BTRFS info (device zram1): flagging fs with big metadata feature
[   65.968302] BTRFS info (device zram1): enabling ssd optimizations
[   65.969551] BTRFS info (device zram1): checking UUID tree
rapido1:/#
Johannes Thumshirn May 27, 2020, 2:01 p.m. UTC | #3
On 27/05/2020 15:55, Johannes Thumshirn wrote:
> On 27/05/2020 15:25, David Sterba wrote:
>> On Thu, May 14, 2020 at 11:24:14AM +0200, Johannes Thumshirn wrote:
>>> From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
>>> 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
>>
>> I tried to follow the example but the filesystem does not mount. But
>> what almost shocked me was the way the key is specified on the userspace
>> side.

OK I think I know what happened. Did I forget to send a v2 of the progs side?
I changed the csum_type number to not have holes in the array, this obviously needs
to be done in progs as well.
Johannes Thumshirn May 27, 2020, 6:04 p.m. UTC | #4
On 27/05/2020 15:25, David Sterba wrote:
> The key for all userspace commands needs to be specified the same way as
> for kernel, ie. "--auth-key btrfs:foo" and use the appropriate ioctls to
> read the key bytes.

Up to now I haven't been able to add a key to the kernel's keyring which 
can be read back to user-space.

How about passing in a key file, like it is done in UBIFS? Should be doable
both with libsodium and libgcrypt.
David Sterba June 1, 2020, 2:30 p.m. UTC | #5
On Wed, May 27, 2020 at 06:04:57PM +0000, Johannes Thumshirn wrote:
> On 27/05/2020 15:25, David Sterba wrote:
> > The key for all userspace commands needs to be specified the same way as
> > for kernel, ie. "--auth-key btrfs:foo" and use the appropriate ioctls to
> > read the key bytes.
> 
> Up to now I haven't been able to add a key to the kernel's keyring which 
> can be read back to user-space.

This needs permissions on the key and I think keys from some keyrings
cannot be read back even with the permissions set. There's an ioctl
equivalent of 'keyctl read' which I used to emulate the reading. Setting
the permissions is cumbersome, as it needs to manually craft the hexa
value, but otherwise would seems a better way than either specifying the
key payload on command line or storing it in a file.

I've looked at other projects using keys, eg. ecryptfs-utils, it uses
keyctl_read_alloc, so that seems to be the preferred way.

> How about passing in a key file, like it is done in UBIFS? Should be doable
> both with libsodium and libgcrypt.

Keyfile would be better, that's what dm-crypt uses, but still.
David Sterba June 1, 2020, 2:35 p.m. UTC | #6
On Wed, May 27, 2020 at 06:04:57PM +0000, Johannes Thumshirn wrote:
> On 27/05/2020 15:25, David Sterba wrote:
> > The key for all userspace commands needs to be specified the same way as
> > for kernel, ie. "--auth-key btrfs:foo" and use the appropriate ioctls to
> > read the key bytes.
> 
> Up to now I haven't been able to add a key to the kernel's keyring which 
> can be read back to user-space.

I was researching a possibility to use libkcapi, the API to use kernel
crypto implementaion, in order to avoid passing the raw key to userspace
completely. Basically, setting up what hash and key to use, pass the
buffer and get back the hash. API-wise it's just one more line to
specify the key -- by the numerical id. But no such interface is there,
only the raw bytes translating the request to the .setkey callback.
diff mbox series

Patch

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 <linux/error-injection.h>
 #include <linux/crc32c.h>
 #include <linux/sched/mm.h>
+#include <keys/user-type.h>
 #include <asm/unaligned.h>
 #include <crypto/hash.h>
 #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,
 };
 
 /*