diff mbox series

[RFC,v10,11/17] dm-verity: consume root hash digest and signature data via LSM hook

Message ID 1687986571-16823-12-git-send-email-wufan@linux.microsoft.com (mailing list archive)
State Not Applicable
Headers show
Series Integrity Policy Enforcement LSM (IPE) | expand

Commit Message

Fan Wu June 28, 2023, 9:09 p.m. UTC
From: Deven Bowers <deven.desai@linux.microsoft.com>

dm-verity provides a strong guarantee of a block device's integrity. As
a generic way to check the integrity of a block device, it provides
those integrity guarantees to its higher layers, including the filesystem
level.

An LSM that control access to a resource on the system based on the
available integrity claims can use this transitive property of
dm-verity, by querying the underlying block_device of a particular
file.

The digest and signature information need to be stored in the block
device to fulfill the next requirement of authorization via LSM policy.
This will enable the LSM to perform revocation of devices that are still
mounted, prohibiting execution of files that are no longer authorized
by the LSM in question.

This patch added two security hook calls in dm-verity to save the
dm-verity roothash and the roothash signature to LSM blobs.

Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
---
v2:
  + No Changes

v3:
  + No changes

v4:
  + No changes

v5:
  + No changes

v6:
  + Fix an improper cleanup that can result in
    a leak

v7:
  + Squash patch 08/12, 10/12 to [11/16]
  + Use part0 for block_device, to retrieve the block_device, when
    calling security_bdev_setsecurity

v8:
  + Undo squash of 08/12, 10/12 - separating drivers/md/ from
    security/ & block/
  + Use common-audit function for dmverity_signature.
  + Change implementation for storing the dm-verity digest to use the
    newly introduced dm_verity_digest structure introduced in patch
    14/20.
  + Create new structure, dm_verity_digest, containing digest algorithm,
    size, and digest itself to pass to the LSM layer. V7 was missing the
    algorithm.
  + Create an associated public header containing this new structure and
    the key values for the LSM hook, specific to dm-verity.
  + Additional information added to commit, discussing the layering of
    the changes and how the information passed will be used.

v9:
  + No changes

v10:
  + No changes
---
 drivers/md/dm-verity-target.c     | 25 +++++++++++++++++++++++--
 drivers/md/dm-verity-verify-sig.c | 16 +++++++++++++---
 drivers/md/dm-verity-verify-sig.h | 10 ++++++----
 include/linux/dm-verity.h         | 19 +++++++++++++++++++
 4 files changed, 61 insertions(+), 9 deletions(-)
 create mode 100644 include/linux/dm-verity.h

Comments

Mike Snitzer July 7, 2023, 2:53 p.m. UTC | #1
On Wed, Jun 28 2023 at  5:09P -0400,
Fan Wu <wufan@linux.microsoft.com> wrote:

> From: Deven Bowers <deven.desai@linux.microsoft.com>
> 
> dm-verity provides a strong guarantee of a block device's integrity. As
> a generic way to check the integrity of a block device, it provides
> those integrity guarantees to its higher layers, including the filesystem
> level.
> 
> An LSM that control access to a resource on the system based on the
> available integrity claims can use this transitive property of
> dm-verity, by querying the underlying block_device of a particular
> file.
> 
> The digest and signature information need to be stored in the block
> device to fulfill the next requirement of authorization via LSM policy.
> This will enable the LSM to perform revocation of devices that are still
> mounted, prohibiting execution of files that are no longer authorized
> by the LSM in question.
> 
> This patch added two security hook calls in dm-verity to save the
> dm-verity roothash and the roothash signature to LSM blobs.
> 
> Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
> Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
> ---

> diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
> index 26adcfea0302..54d46b2f2723 100644
> --- a/drivers/md/dm-verity-target.c
> +++ b/drivers/md/dm-verity-target.c
> @@ -1440,6 +1453,15 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
>  	ti->per_io_data_size = roundup(ti->per_io_data_size,
>  				       __alignof__(struct dm_verity_io));
>  
> +	root_digest.digest = v->root_digest;
> +	root_digest.digest_len = v->digest_size;
> +	root_digest.algo = v->alg_name;
> +
> +	r = security_bdev_setsecurity(bdev, DM_VERITY_ROOTHASH_SEC_NAME, &root_digest,
> +				      sizeof(root_digest));
> +	if (r)
> +		goto bad;
> +
>  	verity_verify_sig_opts_cleanup(&verify_args);
>  
>  	dm_audit_log_ctr(DM_MSG_PREFIX, ti, 1);
> diff --git a/drivers/md/dm-verity-verify-sig.c b/drivers/md/dm-verity-verify-sig.c
> index 4836508ea50c..33165dd7470f 100644
> --- a/drivers/md/dm-verity-verify-sig.c
> +++ b/drivers/md/dm-verity-verify-sig.c
> @@ -9,6 +9,9 @@
>  #include <linux/verification.h>
>  #include <keys/user-type.h>
>  #include <linux/module.h>
> +#include <linux/security.h>
> +#include <linux/dm-verity.h>
> +#include "dm-core.h"

Why are you including dm-core.h here?

>  #include "dm-verity.h"
>  #include "dm-verity-verify-sig.h"
>  
> @@ -97,14 +100,17 @@ int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
>   * verify_verify_roothash - Verify the root hash of the verity hash device
>   *			     using builtin trusted keys.
>   *
> + * @bdev: block_device representing the device-mapper created block device.
> + *	  Used by the security hook, to set information about the block_device.
>   * @root_hash: For verity, the roothash/data to be verified.
>   * @root_hash_len: Size of the roothash/data to be verified.
>   * @sig_data: The trusted signature that verifies the roothash/data.
>   * @sig_len: Size of the signature.
>   *
>   */
> -int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
> -			    const void *sig_data, size_t sig_len)
> +int verity_verify_root_hash(struct block_device *bdev, const void *root_hash,
> +			    size_t root_hash_len, const void *sig_data,
> +			    size_t sig_len)
>  {
>  	int ret;
>  
> @@ -126,8 +132,12 @@ int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
>  				NULL,
>  #endif
>  				VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
> +	if (ret)
> +		return ret;
>  
> -	return ret;
> +	return security_bdev_setsecurity(bdev,
> +					 DM_VERITY_SIGNATURE_SEC_NAME,
> +					 sig_data, sig_len);
>  }
>  
>  void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)

Both of your calls to security_bdev_setsecurity() to set your blobs in
the bdev are suspect because you're doing so from the verity_ctr().
The mapped_device has 2 dm_table slots (active and inactive).  The
verity_ctr() becomes part of the inactive slot, there is an extra step
to bind the inactive table to the active table.

This leads to you changing the blobs in the global bdev _before_ the
table is actually active.  It is possible that the inactive table will
simply be removed and the DM verity device put back in service;
leaving your blob(s) in the bdev inconsistent.

This issue has parallels to how we need to defer changing the global
queue_limits associated with a request_queue until _after_ all table
loading is settled and then the update is done just before resuming
the DM device (mapped_device) -- see dm_table_set_restrictions().

Unfortunately, this feels like it may require a new hook in the
target_type struct (e.g. ->finalize())

Mike
Fan Wu July 12, 2023, 3:43 a.m. UTC | #2
On Fri, Jul 07, 2023 at 10:53:45AM -0400, Mike Snitzer wrote:
Thanks for the review!

> On Wed, Jun 28 2023 at  5:09P -0400,
> Fan Wu <wufan@linux.microsoft.com> wrote:
> 
> > From: Deven Bowers <deven.desai@linux.microsoft.com>
> > 
> > dm-verity provides a strong guarantee of a block device's integrity. As
> > a generic way to check the integrity of a block device, it provides
> > those integrity guarantees to its higher layers, including the filesystem
> > level.
> > 
> > An LSM that control access to a resource on the system based on the
> > available integrity claims can use this transitive property of
> > dm-verity, by querying the underlying block_device of a particular
> > file.
> > 
> > The digest and signature information need to be stored in the block
> > device to fulfill the next requirement of authorization via LSM policy.
> > This will enable the LSM to perform revocation of devices that are still
> > mounted, prohibiting execution of files that are no longer authorized
> > by the LSM in question.
> > 
> > This patch added two security hook calls in dm-verity to save the
> > dm-verity roothash and the roothash signature to LSM blobs.
> > 
> > Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
> > Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
> > ---
...
> > diff --git a/drivers/md/dm-verity-verify-sig.c b/drivers/md/dm-verity-verify-sig.c
> > index 4836508ea50c..33165dd7470f 100644
> > --- a/drivers/md/dm-verity-verify-sig.c
> > +++ b/drivers/md/dm-verity-verify-sig.c
> > @@ -9,6 +9,9 @@
> >  #include <linux/verification.h>
> >  #include <keys/user-type.h>
> >  #include <linux/module.h>
> > +#include <linux/security.h>
> > +#include <linux/dm-verity.h>
> > +#include "dm-core.h"
> 
> Why are you including dm-core.h here?
This is used to get the complete definition of struct mapped_device to extract
the struct block_device from it.

> 
> >  #include "dm-verity.h"
> >  #include "dm-verity-verify-sig.h"
> >  
> > @@ -97,14 +100,17 @@ int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
> >   * verify_verify_roothash - Verify the root hash of the verity hash device
> >   *			     using builtin trusted keys.
> >   *
> > + * @bdev: block_device representing the device-mapper created block device.
> > + *	  Used by the security hook, to set information about the block_device.
> >   * @root_hash: For verity, the roothash/data to be verified.
> >   * @root_hash_len: Size of the roothash/data to be verified.
> >   * @sig_data: The trusted signature that verifies the roothash/data.
> >   * @sig_len: Size of the signature.
> >   *
> >   */
> > -int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
> > -			    const void *sig_data, size_t sig_len)
> > +int verity_verify_root_hash(struct block_device *bdev, const void *root_hash,
> > +			    size_t root_hash_len, const void *sig_data,
> > +			    size_t sig_len)
> >  {
> >  	int ret;
> >  
> > @@ -126,8 +132,12 @@ int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
> >  				NULL,
> >  #endif
> >  				VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
> > +	if (ret)
> > +		return ret;
> >  
> > -	return ret;
> > +	return security_bdev_setsecurity(bdev,
> > +					 DM_VERITY_SIGNATURE_SEC_NAME,
> > +					 sig_data, sig_len);
> >  }
> >  
> >  void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
> 
> Both of your calls to security_bdev_setsecurity() to set your blobs in
> the bdev are suspect because you're doing so from the verity_ctr().
> The mapped_device has 2 dm_table slots (active and inactive).  The
> verity_ctr() becomes part of the inactive slot, there is an extra step
> to bind the inactive table to the active table.
> 
> This leads to you changing the blobs in the global bdev _before_ the
> table is actually active.  It is possible that the inactive table will
> simply be removed and the DM verity device put back in service;
> leaving your blob(s) in the bdev inconsistent.
> 
> This issue has parallels to how we need to defer changing the global
> queue_limits associated with a request_queue until _after_ all table
> loading is settled and then the update is done just before resuming
> the DM device (mapped_device) -- see dm_table_set_restrictions().
> 
> Unfortunately, this feels like it may require a new hook in the
> target_type struct (e.g. ->finalize())
> 
> Mike
Thanks for pointing out this issue. We were calling security_bdev_setsecurity()
because the roothash signature data is only available in verity_ctr()
and it is discarded after verity_ctr() finishes.
After digging deeper into the table_load, I realized that we were indeed
wrong here.

Based on my understanding of your suggestion, it seems that the correct
approach would be to save the roothash signature into the struct dm_target
and then invoke security_bdev_setsecurity() before activating
the inactive table in the __bind function (where dm_table_set_restrictions is called).

To facilitate this process, it seems appropriate to introduce a new hook
called finalize() within the struct target_type. This hook would enable
targets to define tasks that need to be completed before activating
a new table.

In our specific case, we would add a finalize hook to the dm-verity module,
allowing us to call security_bdev_setsecurity() and associate the roothash
information in the struct dm_target with the struct block_device of
the struct mapped_device. Is this correct?

Thanks,
- Fan
Paul Moore July 25, 2023, 8:43 p.m. UTC | #3
On Tue, Jul 11, 2023 at 11:43 PM Fan Wu <wufan@linux.microsoft.com> wrote:
> On Fri, Jul 07, 2023 at 10:53:45AM -0400, Mike Snitzer wrote:

...

> > Both of your calls to security_bdev_setsecurity() to set your blobs in
> > the bdev are suspect because you're doing so from the verity_ctr().
> > The mapped_device has 2 dm_table slots (active and inactive).  The
> > verity_ctr() becomes part of the inactive slot, there is an extra step
> > to bind the inactive table to the active table.
> >
> > This leads to you changing the blobs in the global bdev _before_ the
> > table is actually active.  It is possible that the inactive table will
> > simply be removed and the DM verity device put back in service;
> > leaving your blob(s) in the bdev inconsistent.
> >
> > This issue has parallels to how we need to defer changing the global
> > queue_limits associated with a request_queue until _after_ all table
> > loading is settled and then the update is done just before resuming
> > the DM device (mapped_device) -- see dm_table_set_restrictions().
> >
> > Unfortunately, this feels like it may require a new hook in the
> > target_type struct (e.g. ->finalize())
>
> Thanks for pointing out this issue. We were calling security_bdev_setsecurity()
> because the roothash signature data is only available in verity_ctr()
> and it is discarded after verity_ctr() finishes.
> After digging deeper into the table_load, I realized that we were indeed
> wrong here.
>
> Based on my understanding of your suggestion, it seems that the correct
> approach would be to save the roothash signature into the struct dm_target

Would you be doing this with a LSM hook, or would this live in the
device mapper layer?

> and then invoke security_bdev_setsecurity() before activating
> the inactive table in the __bind function (where dm_table_set_restrictions is called).
>
> To facilitate this process, it seems appropriate to introduce a new hook
> called finalize() within the struct target_type. This hook would enable
> targets to define tasks that need to be completed before activating
> a new table.
>
> In our specific case, we would add a finalize hook to the dm-verity module,
> allowing us to call security_bdev_setsecurity() and associate the roothash
> information in the struct dm_target with the struct block_device of
> the struct mapped_device. Is this correct?

Where would the finalize() hook be called?
Fan Wu Aug. 8, 2023, 10:45 p.m. UTC | #4
On Tue, Jul 25, 2023 at 04:43:48PM -0400, Paul Moore wrote:
> On Tue, Jul 11, 2023 at 11:43???PM Fan Wu <wufan@linux.microsoft.com> wrote:
> > On Fri, Jul 07, 2023 at 10:53:45AM -0400, Mike Snitzer wrote:
> 
> ...
> 
> > > Both of your calls to security_bdev_setsecurity() to set your blobs in
> > > the bdev are suspect because you're doing so from the verity_ctr().
> > > The mapped_device has 2 dm_table slots (active and inactive).  The
> > > verity_ctr() becomes part of the inactive slot, there is an extra step
> > > to bind the inactive table to the active table.
> > >
> > > This leads to you changing the blobs in the global bdev _before_ the
> > > table is actually active.  It is possible that the inactive table will
> > > simply be removed and the DM verity device put back in service;
> > > leaving your blob(s) in the bdev inconsistent.
> > >
> > > This issue has parallels to how we need to defer changing the global
> > > queue_limits associated with a request_queue until _after_ all table
> > > loading is settled and then the update is done just before resuming
> > > the DM device (mapped_device) -- see dm_table_set_restrictions().
> > >
> > > Unfortunately, this feels like it may require a new hook in the
> > > target_type struct (e.g. ->finalize())
> >
> > Thanks for pointing out this issue. We were calling security_bdev_setsecurity()
> > because the roothash signature data is only available in verity_ctr()
> > and it is discarded after verity_ctr() finishes.
> > After digging deeper into the table_load, I realized that we were indeed
> > wrong here.
> >
> > Based on my understanding of your suggestion, it seems that the correct
> > approach would be to save the roothash signature into the struct dm_target
> 
Sorry for the delay in responding. It took me a while to test out the design idea
suggested by Mike.

The current implementation is indeed incorrect. However, I've been able to develop
a working prototype that addresses the problem identified in the existing implementation.
I still need some additional time to fine-tune and clean up the prototype.

My goal is to have everything ready and send it out next month.

> Would you be doing this with a LSM hook, or would this live in the
> device mapper layer?
> 
In my implemention, it is a new hook in the device mapper layer. 
The hook is triggered just before activating an inactive table of a mapped device.
So in our case, we use the hook to attached the dm-verity's roothash metadata
to the block_device struct of mapped device.

> > and then invoke security_bdev_setsecurity() before activating
> > the inactive table in the __bind function (where dm_table_set_restrictions is called).
> >
> > To facilitate this process, it seems appropriate to introduce a new hook
> > called finalize() within the struct target_type. This hook would enable
> > targets to define tasks that need to be completed before activating
> > a new table.
> >
> > In our specific case, we would add a finalize hook to the dm-verity module,
> > allowing us to call security_bdev_setsecurity() and associate the roothash
> > information in the struct dm_target with the struct block_device of
> > the struct mapped_device. Is this correct?
> 
> Where would the finalize() hook be called?

It is in the __bind function in drivers/md/dm.c, calling just before 
rcu_assign_pointer(md->map, (void *)t) which activates the inactive table.

-Fan
Alasdair G Kergon Aug. 8, 2023, 11:40 p.m. UTC | #5
On Tue, Aug 08, 2023 at 03:45:03PM -0700, Fan Wu wrote:
> On Tue, Jul 25, 2023 at 04:43:48PM -0400, Paul Moore wrote:
> > Where would the finalize() hook be called?
> 
> It is in the __bind function in drivers/md/dm.c, calling just before 
> rcu_assign_pointer(md->map, (void *)t) which activates the inactive table.
 
That would be after the existing commit point, meaning the table swap
cannot be cancelled there, so is the finalize() you are proposing void()
i.e. designed so it always succeeds?

Alasdair
Fan Wu Aug. 9, 2023, 6:02 p.m. UTC | #6
On Wed, Aug 09, 2023 at 12:40:23AM +0100, Alasdair G Kergon wrote:
> On Tue, Aug 08, 2023 at 03:45:03PM -0700, Fan Wu wrote:
> > On Tue, Jul 25, 2023 at 04:43:48PM -0400, Paul Moore wrote:
> > > Where would the finalize() hook be called?
> > 
> > It is in the __bind function in drivers/md/dm.c, calling just before 
> > rcu_assign_pointer(md->map, (void *)t) which activates the inactive table.
>  
> That would be after the existing commit point, meaning the table swap
> cannot be cancelled there, so is the finalize() you are proposing void()
> i.e. designed so it always succeeds?
> 
> Alasdair

Thanks for the input.

Actually, no, the hook can be failed. I noticed the existing call before rcu_assign_pointer(md->map, (void *)t);
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/md/dm.c#n2255)
can also be failed so I was following the same pattern.

Could you explain a bit more about the "commit point"? It sounds like it might be better to move
the hook call just before the commit point instead.

-Fan
diff mbox series

Patch

diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 26adcfea0302..54d46b2f2723 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -13,6 +13,7 @@ 
  * access behavior.
  */
 
+#include "dm-core.h"
 #include "dm-verity.h"
 #include "dm-verity-fec.h"
 #include "dm-verity-verify-sig.h"
@@ -22,6 +23,9 @@ 
 #include <linux/scatterlist.h>
 #include <linux/string.h>
 #include <linux/jump_label.h>
+#include <linux/security.h>
+#include <linux/dm-verity.h>
+#include <crypto/hash_info.h>
 
 #define DM_MSG_PREFIX			"verity"
 
@@ -1183,6 +1187,8 @@  static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	sector_t hash_position;
 	char dummy;
 	char *root_hash_digest_to_validate;
+	struct block_device *bdev;
+	struct dm_verity_digest root_digest;
 
 	v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
 	if (!v) {
@@ -1225,6 +1231,13 @@  static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	}
 	v->version = num;
 
+	bdev = dm_table_get_md(ti->table)->disk->part0;
+	if (!bdev) {
+		ti->error = "Mapped device lookup failed";
+		r = -ENOMEM;
+		goto bad;
+	}
+
 	r = dm_get_device(ti, argv[1], BLK_OPEN_READ, &v->data_dev);
 	if (r) {
 		ti->error = "Data device lookup failed";
@@ -1357,7 +1370,7 @@  static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	}
 
 	/* Root hash signature is  a optional parameter*/
-	r = verity_verify_root_hash(root_hash_digest_to_validate,
+	r = verity_verify_root_hash(bdev, root_hash_digest_to_validate,
 				    strlen(root_hash_digest_to_validate),
 				    verify_args.sig,
 				    verify_args.sig_size);
@@ -1440,6 +1453,15 @@  static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	ti->per_io_data_size = roundup(ti->per_io_data_size,
 				       __alignof__(struct dm_verity_io));
 
+	root_digest.digest = v->root_digest;
+	root_digest.digest_len = v->digest_size;
+	root_digest.algo = v->alg_name;
+
+	r = security_bdev_setsecurity(bdev, DM_VERITY_ROOTHASH_SEC_NAME, &root_digest,
+				      sizeof(root_digest));
+	if (r)
+		goto bad;
+
 	verity_verify_sig_opts_cleanup(&verify_args);
 
 	dm_audit_log_ctr(DM_MSG_PREFIX, ti, 1);
@@ -1447,7 +1469,6 @@  static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	return 0;
 
 bad:
-
 	verity_verify_sig_opts_cleanup(&verify_args);
 	dm_audit_log_ctr(DM_MSG_PREFIX, ti, 0);
 	verity_dtr(ti);
diff --git a/drivers/md/dm-verity-verify-sig.c b/drivers/md/dm-verity-verify-sig.c
index 4836508ea50c..33165dd7470f 100644
--- a/drivers/md/dm-verity-verify-sig.c
+++ b/drivers/md/dm-verity-verify-sig.c
@@ -9,6 +9,9 @@ 
 #include <linux/verification.h>
 #include <keys/user-type.h>
 #include <linux/module.h>
+#include <linux/security.h>
+#include <linux/dm-verity.h>
+#include "dm-core.h"
 #include "dm-verity.h"
 #include "dm-verity-verify-sig.h"
 
@@ -97,14 +100,17 @@  int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
  * verify_verify_roothash - Verify the root hash of the verity hash device
  *			     using builtin trusted keys.
  *
+ * @bdev: block_device representing the device-mapper created block device.
+ *	  Used by the security hook, to set information about the block_device.
  * @root_hash: For verity, the roothash/data to be verified.
  * @root_hash_len: Size of the roothash/data to be verified.
  * @sig_data: The trusted signature that verifies the roothash/data.
  * @sig_len: Size of the signature.
  *
  */
-int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
-			    const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct block_device *bdev, const void *root_hash,
+			    size_t root_hash_len, const void *sig_data,
+			    size_t sig_len)
 {
 	int ret;
 
@@ -126,8 +132,12 @@  int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
 				NULL,
 #endif
 				VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
+	if (ret)
+		return ret;
 
-	return ret;
+	return security_bdev_setsecurity(bdev,
+					 DM_VERITY_SIGNATURE_SEC_NAME,
+					 sig_data, sig_len);
 }
 
 void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
diff --git a/drivers/md/dm-verity-verify-sig.h b/drivers/md/dm-verity-verify-sig.h
index f36ea92127bf..5c6023fac97b 100644
--- a/drivers/md/dm-verity-verify-sig.h
+++ b/drivers/md/dm-verity-verify-sig.h
@@ -20,8 +20,9 @@  struct dm_verity_sig_opts {
 
 #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 2
 
-int verity_verify_root_hash(const void *data, size_t data_len,
-			    const void *sig_data, size_t sig_len);
+int verity_verify_root_hash(struct block_device *bdev, const void *data,
+			    size_t data_len, const void *sig_data,
+			    size_t sig_len);
 bool verity_verify_is_sig_opt_arg(const char *arg_name);
 
 int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
@@ -34,8 +35,9 @@  void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts);
 
 #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 0
 
-static inline int verity_verify_root_hash(const void *data, size_t data_len,
-					  const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct block_device *bdev, const void *data,
+			    size_t data_len, const void *sig_data,
+			    size_t sig_len)
 {
 	return 0;
 }
diff --git a/include/linux/dm-verity.h b/include/linux/dm-verity.h
new file mode 100644
index 000000000000..bb0413d55d72
--- /dev/null
+++ b/include/linux/dm-verity.h
@@ -0,0 +1,19 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_DM_VERITY_H
+#define _LINUX_DM_VERITY_H
+
+#include <linux/types.h>
+#include <crypto/hash_info.h>
+#include <linux/device-mapper.h>
+
+struct dm_verity_digest {
+	const char *algo;
+	const u8 *digest;
+	size_t digest_len;
+};
+
+#define DM_VERITY_SIGNATURE_SEC_NAME DM_NAME	".verity-signature"
+#define DM_VERITY_ROOTHASH_SEC_NAME  DM_NAME	".verity-roothash"
+
+#endif /* _LINUX_DM_VERITY_H */