Skip to content

Commit a6af7bc

Browse files
bowerscd-corppcmoore
authored andcommitted
dm-verity: expose root hash digest and signature data to LSMs
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. However, critical security metadata like the dm-verity roothash and its signing information are not easily accessible to the LSMs. To address this limitation, this patch introduces a mechanism to store and manage these essential security details within a newly added LSM blob in the block_device structure. This addition allows LSMs to make access control decisions on the integrity data stored within the block_device, enabling more flexible security policies. For instance, LSMs can now revoke access to dm-verity devices based on their roothashes, ensuring that only authorized and verified content is accessible. Additionally, LSMs can enforce policies to only allow files from dm-verity devices that have a valid digital signature to execute, effectively blocking any unsigned files from execution, thus enhancing security against unauthorized modifications. The patch includes new hook calls, `security_bdev_setintegrity()`, in dm-verity to expose the dm-verity roothash and the roothash signature to LSMs via preresume() callback. By using the preresume() callback, it ensures that the security metadata is consistently in sync with the metadata of the dm-verity target in the current active mapping table. The hook calls are depended on CONFIG_SECURITY. Signed-off-by: Deven Bowers <[email protected]> Signed-off-by: Fan Wu <[email protected]> Reviewed-by: Mikulas Patocka <[email protected]> [PM: moved sig_size field as discussed] Signed-off-by: Paul Moore <[email protected]>
1 parent b55d26b commit a6af7bc

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

drivers/md/dm-verity-target.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/scatterlist.h>
2323
#include <linux/string.h>
2424
#include <linux/jump_label.h>
25+
#include <linux/security.h>
2526

2627
#define DM_MSG_PREFIX "verity"
2728

@@ -930,6 +931,41 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
930931
limits->dma_alignment = limits->logical_block_size - 1;
931932
}
932933

934+
#ifdef CONFIG_SECURITY
935+
936+
static int verity_init_sig(struct dm_verity *v, const void *sig,
937+
size_t sig_size)
938+
{
939+
v->sig_size = sig_size;
940+
941+
if (sig) {
942+
v->root_digest_sig = kmemdup(sig, v->sig_size, GFP_KERNEL);
943+
if (!v->root_digest_sig)
944+
return -ENOMEM;
945+
}
946+
947+
return 0;
948+
}
949+
950+
static void verity_free_sig(struct dm_verity *v)
951+
{
952+
kfree(v->root_digest_sig);
953+
}
954+
955+
#else
956+
957+
static inline int verity_init_sig(struct dm_verity *v, const void *sig,
958+
size_t sig_size)
959+
{
960+
return 0;
961+
}
962+
963+
static inline void verity_free_sig(struct dm_verity *v)
964+
{
965+
}
966+
967+
#endif /* CONFIG_SECURITY */
968+
933969
static void verity_dtr(struct dm_target *ti)
934970
{
935971
struct dm_verity *v = ti->private;
@@ -949,6 +985,7 @@ static void verity_dtr(struct dm_target *ti)
949985
kfree(v->initial_hashstate);
950986
kfree(v->root_digest);
951987
kfree(v->zero_digest);
988+
verity_free_sig(v);
952989

953990
if (v->ahash_tfm) {
954991
static_branch_dec(&ahash_enabled);
@@ -1418,6 +1455,13 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
14181455
ti->error = "Root hash verification failed";
14191456
goto bad;
14201457
}
1458+
1459+
r = verity_init_sig(v, verify_args.sig, verify_args.sig_size);
1460+
if (r < 0) {
1461+
ti->error = "Cannot allocate root digest signature";
1462+
goto bad;
1463+
}
1464+
14211465
v->hash_per_block_bits =
14221466
__fls((1 << v->hash_dev_block_bits) / v->digest_size);
14231467

@@ -1559,8 +1603,79 @@ int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest, unsigned i
15591603
return 0;
15601604
}
15611605

1606+
#ifdef CONFIG_SECURITY
1607+
1608+
#ifdef CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG
1609+
1610+
static int verity_security_set_signature(struct block_device *bdev,
1611+
struct dm_verity *v)
1612+
{
1613+
/*
1614+
* if the dm-verity target is unsigned, v->root_digest_sig will
1615+
* be NULL, and the hook call is still required to let LSMs mark
1616+
* the device as unsigned. This information is crucial for LSMs to
1617+
* block operations such as execution on unsigned files
1618+
*/
1619+
return security_bdev_setintegrity(bdev,
1620+
LSM_INT_DMVERITY_SIG_VALID,
1621+
v->root_digest_sig,
1622+
v->sig_size);
1623+
}
1624+
1625+
#else
1626+
1627+
static inline int verity_security_set_signature(struct block_device *bdev,
1628+
struct dm_verity *v)
1629+
{
1630+
return 0;
1631+
}
1632+
1633+
#endif /* CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG */
1634+
1635+
/*
1636+
* Expose verity target's root hash and signature data to LSMs before resume.
1637+
*
1638+
* Returns 0 on success, or -ENOMEM if the system is out of memory.
1639+
*/
1640+
static int verity_preresume(struct dm_target *ti)
1641+
{
1642+
struct block_device *bdev;
1643+
struct dm_verity_digest root_digest;
1644+
struct dm_verity *v;
1645+
int r;
1646+
1647+
v = ti->private;
1648+
bdev = dm_disk(dm_table_get_md(ti->table))->part0;
1649+
root_digest.digest = v->root_digest;
1650+
root_digest.digest_len = v->digest_size;
1651+
if (static_branch_unlikely(&ahash_enabled) && !v->shash_tfm)
1652+
root_digest.alg = crypto_ahash_alg_name(v->ahash_tfm);
1653+
else
1654+
root_digest.alg = crypto_shash_alg_name(v->shash_tfm);
1655+
1656+
r = security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, &root_digest,
1657+
sizeof(root_digest));
1658+
if (r)
1659+
return r;
1660+
1661+
r = verity_security_set_signature(bdev, v);
1662+
if (r)
1663+
goto bad;
1664+
1665+
return 0;
1666+
1667+
bad:
1668+
1669+
security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, NULL, 0);
1670+
1671+
return r;
1672+
}
1673+
1674+
#endif /* CONFIG_SECURITY */
1675+
15621676
static struct target_type verity_target = {
15631677
.name = "verity",
1678+
/* Note: the LSMs depend on the singleton and immutable features */
15641679
.features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE,
15651680
.version = {1, 10, 0},
15661681
.module = THIS_MODULE,
@@ -1571,6 +1686,9 @@ static struct target_type verity_target = {
15711686
.prepare_ioctl = verity_prepare_ioctl,
15721687
.iterate_devices = verity_iterate_devices,
15731688
.io_hints = verity_io_hints,
1689+
#ifdef CONFIG_SECURITY
1690+
.preresume = verity_preresume,
1691+
#endif /* CONFIG_SECURITY */
15741692
};
15751693
module_dm(verity);
15761694

drivers/md/dm-verity.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ struct dm_verity {
4545
u8 *salt; /* salt: its size is salt_size */
4646
u8 *initial_hashstate; /* salted initial state, if shash_tfm is set */
4747
u8 *zero_digest; /* digest for a zero block */
48+
#ifdef CONFIG_SECURITY
49+
u8 *root_digest_sig; /* signature of the root digest */
50+
unsigned int sig_size; /* root digest signature size */
51+
#endif /* CONFIG_SECURITY */
4852
unsigned int salt_size;
4953
sector_t data_start; /* data offset in 512-byte sectors */
5054
sector_t hash_start; /* hash start in blocks */

include/linux/security.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,15 @@ enum lsm_event {
8383
LSM_POLICY_CHANGE,
8484
};
8585

86+
struct dm_verity_digest {
87+
const char *alg;
88+
const u8 *digest;
89+
size_t digest_len;
90+
};
91+
8692
enum lsm_integrity_type {
87-
__LSM_INT_MAX
93+
LSM_INT_DMVERITY_SIG_VALID,
94+
LSM_INT_DMVERITY_ROOTHASH,
8895
};
8996

9097
/*

0 commit comments

Comments
 (0)