diff mbox series

[RFC,v7,11/16] ipe: add support for dm-verity as a trust provider

Message ID 1634151995-16266-12-git-send-email-deven.desai@linux.microsoft.com (mailing list archive)
State New, archived
Headers show
Series Integrity Policy Enforcement (IPE) | expand

Commit Message

Deven Bowers Oct. 13, 2021, 7:06 p.m. UTC
From: Deven Bowers <deven.desai@linux.microsoft.com>

Allows author of IPE policy to indicate trust for a singular dm-verity
volume, identified by roothash, through "dmverity_roothash" and all
signed dm-verity volumes, through "dmverity_signature".

Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---

Relevant changes since v6:
  * Squash patch 08/12, 10/12 to [11/16]

---
 security/ipe/eval.c                       |  5 ++
 security/ipe/eval.h                       | 10 +++
 security/ipe/hooks.c                      | 48 ++++++++++++++
 security/ipe/hooks.h                      |  6 ++
 security/ipe/ipe.c                        |  9 +++
 security/ipe/ipe.h                        |  3 +
 security/ipe/modules/Kconfig              | 23 +++++++
 security/ipe/modules/Makefile             |  2 +
 security/ipe/modules/dmverity_roothash.c  | 80 +++++++++++++++++++++++
 security/ipe/modules/dmverity_signature.c | 25 +++++++
 10 files changed, 211 insertions(+)
 create mode 100644 security/ipe/modules/dmverity_roothash.c
 create mode 100644 security/ipe/modules/dmverity_signature.c

Comments

Roberto Sassu Nov. 25, 2021, 9:37 a.m. UTC | #1
> From: deven.desai@linux.microsoft.com
> [mailto:deven.desai@linux.microsoft.com]
> Sent: Wednesday, October 13, 2021 9:07 PM
> From: Deven Bowers <deven.desai@linux.microsoft.com>
> 
> Allows author of IPE policy to indicate trust for a singular dm-verity
> volume, identified by roothash, through "dmverity_roothash" and all
> signed dm-verity volumes, through "dmverity_signature".
> 
> Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
> ---
> 
> Relevant changes since v6:
>   * Squash patch 08/12, 10/12 to [11/16]
> 
> ---
>  security/ipe/eval.c                       |  5 ++
>  security/ipe/eval.h                       | 10 +++
>  security/ipe/hooks.c                      | 48 ++++++++++++++
>  security/ipe/hooks.h                      |  6 ++
>  security/ipe/ipe.c                        |  9 +++
>  security/ipe/ipe.h                        |  3 +
>  security/ipe/modules/Kconfig              | 23 +++++++
>  security/ipe/modules/Makefile             |  2 +
>  security/ipe/modules/dmverity_roothash.c  | 80 +++++++++++++++++++++++
>  security/ipe/modules/dmverity_signature.c | 25 +++++++
>  10 files changed, 211 insertions(+)
>  create mode 100644 security/ipe/modules/dmverity_roothash.c
>  create mode 100644 security/ipe/modules/dmverity_signature.c
> 
> diff --git a/security/ipe/eval.c b/security/ipe/eval.c
> index 361efccebad4..facc05c753f4 100644
> --- a/security/ipe/eval.c
> +++ b/security/ipe/eval.c
> @@ -23,6 +23,7 @@ static struct super_block *pinned_sb;
>  static DEFINE_SPINLOCK(pin_lock);
> 
>  #define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb)
> +#define FILE_BLOCK_DEV(f) (FILE_SUPERBLOCK(f)->s_bdev)
> 
>  /**
>   * pin_sb: pin the underlying superblock of @f, marking it as trusted
> @@ -95,6 +96,10 @@ static struct ipe_eval_ctx *build_ctx(const struct file *file,
>  	ctx->hook = hook;
>  	ctx->ci_ctx = ipe_current_ctx();
>  	ctx->from_init_sb = from_pinned(file);
> +	if (file) {
> +		if (FILE_BLOCK_DEV(file))
> +			ctx->ipe_bdev = ipe_bdev(FILE_BLOCK_DEV(file));
> +	}
> 
>  	return ctx;
>  }
> diff --git a/security/ipe/eval.h b/security/ipe/eval.h
> index 42fb7fdf2599..25d2d8d55702 100644
> --- a/security/ipe/eval.h
> +++ b/security/ipe/eval.h
> @@ -13,6 +13,14 @@
>  #include "hooks.h"
>  #include "policy.h"
> 
> +struct ipe_bdev {
> +	const u8       *sigdata;
> +	size_t		siglen;
> +
> +	const u8       *hash;
> +	size_t		hashlen;
> +};
> +
>  struct ipe_eval_ctx {
>  	enum ipe_hook hook;
>  	enum ipe_operation op;
> @@ -20,6 +28,8 @@ struct ipe_eval_ctx {
>  	const struct file *file;
>  	struct ipe_context *ci_ctx;
> 
> +	const struct ipe_bdev *ipe_bdev;
> +
>  	bool from_init_sb;
>  };
> 
> diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
> index 2d4a4f0eead0..470fb48e490c 100644
> --- a/security/ipe/hooks.c
> +++ b/security/ipe/hooks.c
> @@ -13,6 +13,7 @@
>  #include <linux/types.h>
>  #include <linux/refcount.h>
>  #include <linux/rcupdate.h>
> +#include <linux/blk_types.h>
>  #include <linux/binfmts.h>
>  #include <linux/mman.h>
> 
> @@ -219,3 +220,50 @@ void ipe_sb_free_security(struct super_block *mnt_sb)
>  {
>  	ipe_invalidate_pinned_sb(mnt_sb);
>  }
> +
> +/**
> + * ipe_bdev_free_security: free nested structures within IPE's LSM blob
> + *			   in block_devices
> + * @bdev: Supplies a pointer to a block_device that contains the structure
> + *	  to free.
> + */
> +void ipe_bdev_free_security(struct block_device *bdev)
> +{
> +	struct ipe_bdev *blob = ipe_bdev(bdev);
> +
> +	kfree(blob->sigdata);
> +}
> +
> +/**
> + * ipe_bdev_setsecurity: associate some data from the block device layer
> + *			 with IPE's LSM blob.
> + * @bdev: Supplies a pointer to a block_device that contains the LSM blob.
> + * @key: Supplies the string key that uniquely identifies the value.
> + * @value: Supplies the value to store.
> + * @len: The length of @value.
> + */
> +int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
> +			 const void *value, size_t len)
> +{
> +	struct ipe_bdev *blob = ipe_bdev(bdev);
> +
> +	if (!strcmp(key, DM_VERITY_SIGNATURE_SEC_NAME)) {
> +		blob->siglen = len;
> +		blob->sigdata = kmemdup(value, len, GFP_KERNEL);
> +		if (!blob->sigdata)
> +			return -ENOMEM;
> +
> +		return 0;
> +	}
> +
> +	if (!strcmp(key, DM_VERITY_ROOTHASH_SEC_NAME)) {
> +		blob->hashlen = len;
> +		blob->hash = kmemdup(value, len, GFP_KERNEL);
> +		if (!blob->hash)
> +			return -ENOMEM;
> +
> +		return 0;
> +	}
> +
> +	return -ENOSYS;
> +}
> diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
> index e7f107ab5620..285f35187188 100644
> --- a/security/ipe/hooks.h
> +++ b/security/ipe/hooks.h
> @@ -10,6 +10,7 @@
>  #include <linux/sched.h>
>  #include <linux/binfmts.h>
>  #include <linux/security.h>
> +#include <linux/device-mapper.h>
> 
>  enum ipe_hook {
>  	ipe_hook_exec = 0,
> @@ -40,4 +41,9 @@ int ipe_on_kernel_load_data(enum kernel_load_data_id
> id, bool contents);
> 
>  void ipe_sb_free_security(struct super_block *mnt_sb);
> 
> +void ipe_bdev_free_security(struct block_device *bdev);
> +
> +int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
> +			 const void *value, size_t len);
> +
>  #endif /* IPE_HOOKS_H */
> diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
> index 1382d50078ec..215936cb4574 100644
> --- a/security/ipe/ipe.c
> +++ b/security/ipe/ipe.c
> @@ -9,6 +9,7 @@
>  #include "ipe_parser.h"
>  #include "modules/ipe_module.h"
>  #include "modules.h"
> +#include "eval.h"
> 
>  #include <linux/fs.h>
>  #include <linux/sched.h>
> @@ -20,8 +21,14 @@
> 
>  struct lsm_blob_sizes ipe_blobs __lsm_ro_after_init = {
>  	.lbs_task = sizeof(struct ipe_context __rcu *),
> +	.lbs_bdev = sizeof(struct ipe_bdev),
>  };
> 
> +struct ipe_bdev *ipe_bdev(struct block_device *b)
> +{
> +	return b->security + ipe_blobs.lbs_bdev;
> +}
> +
>  static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
>  	LSM_HOOK_INIT(task_alloc, ipe_task_alloc),
>  	LSM_HOOK_INIT(task_free, ipe_task_free),
> @@ -31,6 +38,8 @@ static struct security_hook_list ipe_hooks[]
> __lsm_ro_after_init = {
>  	LSM_HOOK_INIT(kernel_read_file, ipe_on_kernel_read),
>  	LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
>  	LSM_HOOK_INIT(sb_free_security, ipe_sb_free_security),
> +	LSM_HOOK_INIT(bdev_free_security, ipe_bdev_free_security),
> +	LSM_HOOK_INIT(bdev_setsecurity, ipe_bdev_setsecurity),
>  };
> 
>  /**
> diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
> index ad16d2bebfec..6b4c7e07f204 100644
> --- a/security/ipe/ipe.h
> +++ b/security/ipe/ipe.h
> @@ -14,10 +14,13 @@
> 
>  #include <linux/types.h>
>  #include <linux/sched.h>
> +#include <linux/blk_types.h>
>  #include <linux/lsm_hooks.h>
> 
>  extern struct lsm_blob_sizes ipe_blobs;
>  extern struct ipe_parser __start_ipe_parsers[], __end_ipe_parsers[];
>  extern struct ipe_module __start_ipe_modules[], __end_ipe_modules[];
> 
> +struct ipe_bdev *ipe_bdev(struct block_device *b);
> +
>  #endif /* IPE_H */
> diff --git a/security/ipe/modules/Kconfig b/security/ipe/modules/Kconfig
> index fad96ba534e2..a6ea06cf0737 100644
> --- a/security/ipe/modules/Kconfig
> +++ b/security/ipe/modules/Kconfig
> @@ -16,5 +16,28 @@ config IPE_PROP_BOOT_VERIFIED
> 
>  	  If unsure, answer N.
> 
> +config IPE_PROP_DM_VERITY_SIGNATURE
> +	bool "Enable support for signed dm-verity volumes"
> +	depends on DM_VERITY_VERIFY_ROOTHASH_SIG
> +	default Y
> +	help
> +	  This option enables the property 'dmverity_signature' in
> +	  IPE policy. This property evaluates to TRUE when a file
> +	  is evaluated against a dm-verity volume that was mounted
> +	  with a signed root-hash.
> +
> +	  If unsure, answer Y.
> +
> +config IPE_PROP_DM_VERITY_ROOTHASH
> +	bool "Enable support for dm-verity volumes"
> +	depends on DM_VERITY
> +	default Y
> +	help
> +	  This option enables the property 'dmverity_roothash' in
> +	  IPE policy. This property evaluates to TRUE when a file
> +	  is evaluated against a dm-verity volume whose root hash
> +	  matches the supplied value.
> +
> +	  If unsure, answer Y.
> 
>  endmenu
> diff --git a/security/ipe/modules/Makefile b/security/ipe/modules/Makefile
> index e0045ec65434..84fadce85193 100644
> --- a/security/ipe/modules/Makefile
> +++ b/security/ipe/modules/Makefile
> @@ -6,3 +6,5 @@
>  #
> 
>  obj-$(CONFIG_IPE_PROP_BOOT_VERIFIED) += boot_verified.o
> +obj-$(CONFIG_IPE_PROP_DM_VERITY_SIGNATURE) += dmverity_signature.o
> +obj-$(CONFIG_IPE_PROP_DM_VERITY_ROOTHASH) += dmverity_roothash.o
> diff --git a/security/ipe/modules/dmverity_roothash.c
> b/security/ipe/modules/dmverity_roothash.c
> new file mode 100644
> index 000000000000..0f82bec3b842
> --- /dev/null
> +++ b/security/ipe/modules/dmverity_roothash.c
> @@ -0,0 +1,80 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) Microsoft Corporation. All rights reserved.
> + */
> +
> +#include "ipe_module.h"
> +
> +#include <linux/fs.h>
> +#include <linux/types.h>
> +
> +struct counted_array {
> +	size_t	len;
> +	u8     *data;
> +};
> +
> +static int dvrh_parse(const char *valstr, void **value)
> +{
> +	int rv = 0;
> +	struct counted_array *arr;
> +
> +	arr = kzalloc(sizeof(*arr), GFP_KERNEL);
> +	if (!arr) {
> +		rv = -ENOMEM;
> +		goto err;
> +	}
> +
> +	arr->len = (strlen(valstr) / 2);
> +
> +	arr->data = kzalloc(arr->len, GFP_KERNEL);
> +	if (!arr->data) {
> +		rv = -ENOMEM;
> +		goto err;
> +	}
> +
> +	rv = hex2bin(arr->data, valstr, arr->len);
> +	if (rv != 0)
> +		goto err2;
> +
> +	*value = arr;
> +	return rv;
> +err2:
> +	kfree(arr->data);
> +err:
> +	kfree(arr);
> +	return rv;
> +}
> +
> +static bool dvrh_eval(const struct ipe_eval_ctx *ctx, const void *val)
> +{
> +	const u8 *src;
> +	struct counted_array *expect = (struct counted_array *)val;
> +
> +	if (!ctx->ipe_bdev)
> +		return false;
> +
> +	if (ctx->ipe_bdev->hashlen != expect->len)
> +		return false;
> +
> +	src = ctx->ipe_bdev->hash;
> +
> +	return !memcmp(expect->data, src, expect->len);

Hi Deven

I was curious to see if determining the property at run-time
could apply also to dm-verity. It seems it could be done
(I omit some checks, I also keep the expected value in hex
format):

---
        md = dm_get_md(file_inode(ctx->file)->i_sb->s_dev);
        table = dm_get_live_table(md, &srcu_idx);
        num_targets = dm_table_get_num_targets(table);

        for (i = 0; i < num_targets; i++) {
                struct dm_target *ti = dm_table_get_target(table, i);

                if (strcmp(ti->type->name, "verity"))
                        continue;

                ti->type->status(ti, STATUSTYPE_IMA, 0, result, sizeof(result));
        }

        dm_put_live_table(md, srcu_idx);
        dm_put(md);

        root_digest_ptr = strstr(result, "root_digest=");
        return !strncmp(expect->data, root_digest_ptr + 12, expect->len);
---

Only dm_table_get_target() is not exported yet, but I guess it could
be. dm_table_get_num_targets() is exported.

With this code, you would not have to manage security blobs
outside IPE. Maybe you could add a blob for the super block, so
that you verify the dm-verity property just once per filesystem.

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Zhong Ronghua

> +}
> +
> +static int dvrh_free(void **val)
> +{
> +	struct counted_array *expect = (struct counted_array *)val;
> +
> +	kfree(expect->data);
> +	kfree(expect);
> +
> +	return 0;
> +}
> +
> +IPE_MODULE(dvrh) = {
> +	.name = "dmverity_roothash",
> +	.version = 1,
> +	.parse = dvrh_parse,
> +	.free = dvrh_free,
> +	.eval = dvrh_eval,
> +};
> diff --git a/security/ipe/modules/dmverity_signature.c
> b/security/ipe/modules/dmverity_signature.c
> new file mode 100644
> index 000000000000..08746fcbcb3e
> --- /dev/null
> +++ b/security/ipe/modules/dmverity_signature.c
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) Microsoft Corporation. All rights reserved.
> + */
> +
> +#include "ipe_module.h"
> +
> +#include <linux/fs.h>
> +#include <linux/types.h>
> +
> +static bool dvv_eval(const struct ipe_eval_ctx *ctx, const void *val)
> +{
> +	bool expect = (bool)val;
> +	bool eval = ctx->ipe_bdev && (!!ctx->ipe_bdev->sigdata);
> +
> +	return expect == eval;
> +}
> +
> +IPE_MODULE(dvv) = {
> +	.name = "dmverity_signature",
> +	.version = 1,
> +	.parse = ipe_bool_parse,
> +	.free = NULL,
> +	.eval = dvv_eval,
> +};
> --
> 2.33.0
Deven Bowers Nov. 30, 2021, 6:55 p.m. UTC | #2
On 11/25/2021 1:37 AM, Roberto Sassu wrote:
>> From: deven.desai@linux.microsoft.com
>> [mailto:deven.desai@linux.microsoft.com]
>> Sent: Wednesday, October 13, 2021 9:07 PM
>> From: Deven Bowers <deven.desai@linux.microsoft.com>

..snip

>> diff --git a/security/ipe/modules/Makefile b/security/ipe/modules/Makefile
>> index e0045ec65434..84fadce85193 100644
>> --- a/security/ipe/modules/Makefile
>> +++ b/security/ipe/modules/Makefile
>> @@ -6,3 +6,5 @@
>>   #
>>
>>   obj-$(CONFIG_IPE_PROP_BOOT_VERIFIED) += boot_verified.o
>> +obj-$(CONFIG_IPE_PROP_DM_VERITY_SIGNATURE) += dmverity_signature.o
>> +obj-$(CONFIG_IPE_PROP_DM_VERITY_ROOTHASH) += dmverity_roothash.o
>> diff --git a/security/ipe/modules/dmverity_roothash.c
>> b/security/ipe/modules/dmverity_roothash.c
>> new file mode 100644
>> index 000000000000..0f82bec3b842
>> --- /dev/null
>> +++ b/security/ipe/modules/dmverity_roothash.c
>> @@ -0,0 +1,80 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) Microsoft Corporation. All rights reserved.
>> + */
>> +
>> +#include "ipe_module.h"
>> +
>> +#include <linux/fs.h>
>> +#include <linux/types.h>
>> +
>> +struct counted_array {
>> +	size_t	len;
>> +	u8     *data;
>> +};
>> +
>> +static int dvrh_parse(const char *valstr, void **value)
>> +{
>> +	int rv = 0;
>> +	struct counted_array *arr;
>> +
>> +	arr = kzalloc(sizeof(*arr), GFP_KERNEL);
>> +	if (!arr) {
>> +		rv = -ENOMEM;
>> +		goto err;
>> +	}
>> +
>> +	arr->len = (strlen(valstr) / 2);
>> +
>> +	arr->data = kzalloc(arr->len, GFP_KERNEL);
>> +	if (!arr->data) {
>> +		rv = -ENOMEM;
>> +		goto err;
>> +	}
>> +
>> +	rv = hex2bin(arr->data, valstr, arr->len);
>> +	if (rv != 0)
>> +		goto err2;
>> +
>> +	*value = arr;
>> +	return rv;
>> +err2:
>> +	kfree(arr->data);
>> +err:
>> +	kfree(arr);
>> +	return rv;
>> +}
>> +
>> +static bool dvrh_eval(const struct ipe_eval_ctx *ctx, const void *val)
>> +{
>> +	const u8 *src;
>> +	struct counted_array *expect = (struct counted_array *)val;
>> +
>> +	if (!ctx->ipe_bdev)
>> +		return false;
>> +
>> +	if (ctx->ipe_bdev->hashlen != expect->len)
>> +		return false;
>> +
>> +	src = ctx->ipe_bdev->hash;
>> +
>> +	return !memcmp(expect->data, src, expect->len);
> Hi Deven
>
> I was curious to see if determining the property at run-time
> could apply also to dm-verity. It seems it could be done
> (I omit some checks, I also keep the expected value in hex
> format):
>
> ---
>          md = dm_get_md(file_inode(ctx->file)->i_sb->s_dev);
>          table = dm_get_live_table(md, &srcu_idx);
>          num_targets = dm_table_get_num_targets(table);
>
>          for (i = 0; i < num_targets; i++) {
>                  struct dm_target *ti = dm_table_get_target(table, i);
>
>                  if (strcmp(ti->type->name, "verity"))
>                          continue;
>
>                  ti->type->status(ti, STATUSTYPE_IMA, 0, result, sizeof(result));
>          }
>
>          dm_put_live_table(md, srcu_idx);
>          dm_put(md);
>
>          root_digest_ptr = strstr(result, "root_digest=");
>          return !strncmp(expect->data, root_digest_ptr + 12, expect->len);
> ---
>
> Only dm_table_get_target() is not exported yet, but I guess it could
> be. dm_table_get_num_targets() is exported.

I had tried something similar in a very early draft of IPE. The issue
that comes with this is that when you compile device-mapper as a module
(CONFIG_BLK_DEV_DM=m) you start to get linking errors with this
approach.

Obviously, we can fix this in the IPE's module Kconfig by setting the
dependency to be =y, but it's something to highlight. My general
preference is to support the =m configuration by using these blobs.

The runtime approach does work with fs-verity, because fs-verity is a
file-system level feature that cannot be compiled as a module.

> With this code, you would not have to manage security blobs
> outside IPE. Maybe you could add a blob for the super block, so
> that you verify the dm-verity property just once per filesystem.
>
> Roberto
>
> HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
> Managing Director: Li Peng, Zhong Ronghua
>
>> +}
>> +
>> +static int dvrh_free(void **val)
>> +{
>> +	struct counted_array *expect = (struct counted_array *)val;
>> +
>> +	kfree(expect->data);
>> +	kfree(expect);
>> +
>> +	return 0;
>> +}
>> +
>> +IPE_MODULE(dvrh) = {
>> +	.name = "dmverity_roothash",
>> +	.version = 1,
>> +	.parse = dvrh_parse,
>> +	.free = dvrh_free,
>> +	.eval = dvrh_eval,
>> +};
>> diff --git a/security/ipe/modules/dmverity_signature.c
>> b/security/ipe/modules/dmverity_signature.c
>> new file mode 100644
>> index 000000000000..08746fcbcb3e
>> --- /dev/null
>> +++ b/security/ipe/modules/dmverity_signature.c
>> @@ -0,0 +1,25 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) Microsoft Corporation. All rights reserved.
>> + */
>> +
>> +#include "ipe_module.h"
>> +
>> +#include <linux/fs.h>
>> +#include <linux/types.h>
>> +
>> +static bool dvv_eval(const struct ipe_eval_ctx *ctx, const void *val)
>> +{
>> +	bool expect = (bool)val;
>> +	bool eval = ctx->ipe_bdev && (!!ctx->ipe_bdev->sigdata);
>> +
>> +	return expect == eval;
>> +}
>> +
>> +IPE_MODULE(dvv) = {
>> +	.name = "dmverity_signature",
>> +	.version = 1,
>> +	.parse = ipe_bool_parse,
>> +	.free = NULL,
>> +	.eval = dvv_eval,
>> +};
>> --
>> 2.33.0
diff mbox series

Patch

diff --git a/security/ipe/eval.c b/security/ipe/eval.c
index 361efccebad4..facc05c753f4 100644
--- a/security/ipe/eval.c
+++ b/security/ipe/eval.c
@@ -23,6 +23,7 @@  static struct super_block *pinned_sb;
 static DEFINE_SPINLOCK(pin_lock);
 
 #define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb)
+#define FILE_BLOCK_DEV(f) (FILE_SUPERBLOCK(f)->s_bdev)
 
 /**
  * pin_sb: pin the underlying superblock of @f, marking it as trusted
@@ -95,6 +96,10 @@  static struct ipe_eval_ctx *build_ctx(const struct file *file,
 	ctx->hook = hook;
 	ctx->ci_ctx = ipe_current_ctx();
 	ctx->from_init_sb = from_pinned(file);
+	if (file) {
+		if (FILE_BLOCK_DEV(file))
+			ctx->ipe_bdev = ipe_bdev(FILE_BLOCK_DEV(file));
+	}
 
 	return ctx;
 }
diff --git a/security/ipe/eval.h b/security/ipe/eval.h
index 42fb7fdf2599..25d2d8d55702 100644
--- a/security/ipe/eval.h
+++ b/security/ipe/eval.h
@@ -13,6 +13,14 @@ 
 #include "hooks.h"
 #include "policy.h"
 
+struct ipe_bdev {
+	const u8       *sigdata;
+	size_t		siglen;
+
+	const u8       *hash;
+	size_t		hashlen;
+};
+
 struct ipe_eval_ctx {
 	enum ipe_hook hook;
 	enum ipe_operation op;
@@ -20,6 +28,8 @@  struct ipe_eval_ctx {
 	const struct file *file;
 	struct ipe_context *ci_ctx;
 
+	const struct ipe_bdev *ipe_bdev;
+
 	bool from_init_sb;
 };
 
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
index 2d4a4f0eead0..470fb48e490c 100644
--- a/security/ipe/hooks.c
+++ b/security/ipe/hooks.c
@@ -13,6 +13,7 @@ 
 #include <linux/types.h>
 #include <linux/refcount.h>
 #include <linux/rcupdate.h>
+#include <linux/blk_types.h>
 #include <linux/binfmts.h>
 #include <linux/mman.h>
 
@@ -219,3 +220,50 @@  void ipe_sb_free_security(struct super_block *mnt_sb)
 {
 	ipe_invalidate_pinned_sb(mnt_sb);
 }
+
+/**
+ * ipe_bdev_free_security: free nested structures within IPE's LSM blob
+ *			   in block_devices
+ * @bdev: Supplies a pointer to a block_device that contains the structure
+ *	  to free.
+ */
+void ipe_bdev_free_security(struct block_device *bdev)
+{
+	struct ipe_bdev *blob = ipe_bdev(bdev);
+
+	kfree(blob->sigdata);
+}
+
+/**
+ * ipe_bdev_setsecurity: associate some data from the block device layer
+ *			 with IPE's LSM blob.
+ * @bdev: Supplies a pointer to a block_device that contains the LSM blob.
+ * @key: Supplies the string key that uniquely identifies the value.
+ * @value: Supplies the value to store.
+ * @len: The length of @value.
+ */
+int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
+			 const void *value, size_t len)
+{
+	struct ipe_bdev *blob = ipe_bdev(bdev);
+
+	if (!strcmp(key, DM_VERITY_SIGNATURE_SEC_NAME)) {
+		blob->siglen = len;
+		blob->sigdata = kmemdup(value, len, GFP_KERNEL);
+		if (!blob->sigdata)
+			return -ENOMEM;
+
+		return 0;
+	}
+
+	if (!strcmp(key, DM_VERITY_ROOTHASH_SEC_NAME)) {
+		blob->hashlen = len;
+		blob->hash = kmemdup(value, len, GFP_KERNEL);
+		if (!blob->hash)
+			return -ENOMEM;
+
+		return 0;
+	}
+
+	return -ENOSYS;
+}
diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
index e7f107ab5620..285f35187188 100644
--- a/security/ipe/hooks.h
+++ b/security/ipe/hooks.h
@@ -10,6 +10,7 @@ 
 #include <linux/sched.h>
 #include <linux/binfmts.h>
 #include <linux/security.h>
+#include <linux/device-mapper.h>
 
 enum ipe_hook {
 	ipe_hook_exec = 0,
@@ -40,4 +41,9 @@  int ipe_on_kernel_load_data(enum kernel_load_data_id id, bool contents);
 
 void ipe_sb_free_security(struct super_block *mnt_sb);
 
+void ipe_bdev_free_security(struct block_device *bdev);
+
+int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
+			 const void *value, size_t len);
+
 #endif /* IPE_HOOKS_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 1382d50078ec..215936cb4574 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -9,6 +9,7 @@ 
 #include "ipe_parser.h"
 #include "modules/ipe_module.h"
 #include "modules.h"
+#include "eval.h"
 
 #include <linux/fs.h>
 #include <linux/sched.h>
@@ -20,8 +21,14 @@ 
 
 struct lsm_blob_sizes ipe_blobs __lsm_ro_after_init = {
 	.lbs_task = sizeof(struct ipe_context __rcu *),
+	.lbs_bdev = sizeof(struct ipe_bdev),
 };
 
+struct ipe_bdev *ipe_bdev(struct block_device *b)
+{
+	return b->security + ipe_blobs.lbs_bdev;
+}
+
 static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(task_alloc, ipe_task_alloc),
 	LSM_HOOK_INIT(task_free, ipe_task_free),
@@ -31,6 +38,8 @@  static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(kernel_read_file, ipe_on_kernel_read),
 	LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
 	LSM_HOOK_INIT(sb_free_security, ipe_sb_free_security),
+	LSM_HOOK_INIT(bdev_free_security, ipe_bdev_free_security),
+	LSM_HOOK_INIT(bdev_setsecurity, ipe_bdev_setsecurity),
 };
 
 /**
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
index ad16d2bebfec..6b4c7e07f204 100644
--- a/security/ipe/ipe.h
+++ b/security/ipe/ipe.h
@@ -14,10 +14,13 @@ 
 
 #include <linux/types.h>
 #include <linux/sched.h>
+#include <linux/blk_types.h>
 #include <linux/lsm_hooks.h>
 
 extern struct lsm_blob_sizes ipe_blobs;
 extern struct ipe_parser __start_ipe_parsers[], __end_ipe_parsers[];
 extern struct ipe_module __start_ipe_modules[], __end_ipe_modules[];
 
+struct ipe_bdev *ipe_bdev(struct block_device *b);
+
 #endif /* IPE_H */
diff --git a/security/ipe/modules/Kconfig b/security/ipe/modules/Kconfig
index fad96ba534e2..a6ea06cf0737 100644
--- a/security/ipe/modules/Kconfig
+++ b/security/ipe/modules/Kconfig
@@ -16,5 +16,28 @@  config IPE_PROP_BOOT_VERIFIED
 
 	  If unsure, answer N.
 
+config IPE_PROP_DM_VERITY_SIGNATURE
+	bool "Enable support for signed dm-verity volumes"
+	depends on DM_VERITY_VERIFY_ROOTHASH_SIG
+	default Y
+	help
+	  This option enables the property 'dmverity_signature' in
+	  IPE policy. This property evaluates to TRUE when a file
+	  is evaluated against a dm-verity volume that was mounted
+	  with a signed root-hash.
+
+	  If unsure, answer Y.
+
+config IPE_PROP_DM_VERITY_ROOTHASH
+	bool "Enable support for dm-verity volumes"
+	depends on DM_VERITY
+	default Y
+	help
+	  This option enables the property 'dmverity_roothash' in
+	  IPE policy. This property evaluates to TRUE when a file
+	  is evaluated against a dm-verity volume whose root hash
+	  matches the supplied value.
+
+	  If unsure, answer Y.
 
 endmenu
diff --git a/security/ipe/modules/Makefile b/security/ipe/modules/Makefile
index e0045ec65434..84fadce85193 100644
--- a/security/ipe/modules/Makefile
+++ b/security/ipe/modules/Makefile
@@ -6,3 +6,5 @@ 
 #
 
 obj-$(CONFIG_IPE_PROP_BOOT_VERIFIED) += boot_verified.o
+obj-$(CONFIG_IPE_PROP_DM_VERITY_SIGNATURE) += dmverity_signature.o
+obj-$(CONFIG_IPE_PROP_DM_VERITY_ROOTHASH) += dmverity_roothash.o
diff --git a/security/ipe/modules/dmverity_roothash.c b/security/ipe/modules/dmverity_roothash.c
new file mode 100644
index 000000000000..0f82bec3b842
--- /dev/null
+++ b/security/ipe/modules/dmverity_roothash.c
@@ -0,0 +1,80 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe_module.h"
+
+#include <linux/fs.h>
+#include <linux/types.h>
+
+struct counted_array {
+	size_t	len;
+	u8     *data;
+};
+
+static int dvrh_parse(const char *valstr, void **value)
+{
+	int rv = 0;
+	struct counted_array *arr;
+
+	arr = kzalloc(sizeof(*arr), GFP_KERNEL);
+	if (!arr) {
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	arr->len = (strlen(valstr) / 2);
+
+	arr->data = kzalloc(arr->len, GFP_KERNEL);
+	if (!arr->data) {
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	rv = hex2bin(arr->data, valstr, arr->len);
+	if (rv != 0)
+		goto err2;
+
+	*value = arr;
+	return rv;
+err2:
+	kfree(arr->data);
+err:
+	kfree(arr);
+	return rv;
+}
+
+static bool dvrh_eval(const struct ipe_eval_ctx *ctx, const void *val)
+{
+	const u8 *src;
+	struct counted_array *expect = (struct counted_array *)val;
+
+	if (!ctx->ipe_bdev)
+		return false;
+
+	if (ctx->ipe_bdev->hashlen != expect->len)
+		return false;
+
+	src = ctx->ipe_bdev->hash;
+
+	return !memcmp(expect->data, src, expect->len);
+}
+
+static int dvrh_free(void **val)
+{
+	struct counted_array *expect = (struct counted_array *)val;
+
+	kfree(expect->data);
+	kfree(expect);
+
+	return 0;
+}
+
+IPE_MODULE(dvrh) = {
+	.name = "dmverity_roothash",
+	.version = 1,
+	.parse = dvrh_parse,
+	.free = dvrh_free,
+	.eval = dvrh_eval,
+};
diff --git a/security/ipe/modules/dmverity_signature.c b/security/ipe/modules/dmverity_signature.c
new file mode 100644
index 000000000000..08746fcbcb3e
--- /dev/null
+++ b/security/ipe/modules/dmverity_signature.c
@@ -0,0 +1,25 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe_module.h"
+
+#include <linux/fs.h>
+#include <linux/types.h>
+
+static bool dvv_eval(const struct ipe_eval_ctx *ctx, const void *val)
+{
+	bool expect = (bool)val;
+	bool eval = ctx->ipe_bdev && (!!ctx->ipe_bdev->sigdata);
+
+	return expect == eval;
+}
+
+IPE_MODULE(dvv) = {
+	.name = "dmverity_signature",
+	.version = 1,
+	.parse = ipe_bool_parse,
+	.free = NULL,
+	.eval = dvv_eval,
+};