Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
Reviewed-by: Mikulas Patocka <mpatocka@redhat.com>
[PM: moved sig_size field as discussed]
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Deven Bowers and committed by
Paul Moore
a6af7bc3 b55d26bd

+130 -1
+118
drivers/md/dm-verity-target.c
··· 22 22 #include <linux/scatterlist.h> 23 23 #include <linux/string.h> 24 24 #include <linux/jump_label.h> 25 + #include <linux/security.h> 25 26 26 27 #define DM_MSG_PREFIX "verity" 27 28 ··· 931 930 limits->dma_alignment = limits->logical_block_size - 1; 932 931 } 933 932 933 + #ifdef CONFIG_SECURITY 934 + 935 + static int verity_init_sig(struct dm_verity *v, const void *sig, 936 + size_t sig_size) 937 + { 938 + v->sig_size = sig_size; 939 + 940 + if (sig) { 941 + v->root_digest_sig = kmemdup(sig, v->sig_size, GFP_KERNEL); 942 + if (!v->root_digest_sig) 943 + return -ENOMEM; 944 + } 945 + 946 + return 0; 947 + } 948 + 949 + static void verity_free_sig(struct dm_verity *v) 950 + { 951 + kfree(v->root_digest_sig); 952 + } 953 + 954 + #else 955 + 956 + static inline int verity_init_sig(struct dm_verity *v, const void *sig, 957 + size_t sig_size) 958 + { 959 + return 0; 960 + } 961 + 962 + static inline void verity_free_sig(struct dm_verity *v) 963 + { 964 + } 965 + 966 + #endif /* CONFIG_SECURITY */ 967 + 934 968 static void verity_dtr(struct dm_target *ti) 935 969 { 936 970 struct dm_verity *v = ti->private; ··· 985 949 kfree(v->initial_hashstate); 986 950 kfree(v->root_digest); 987 951 kfree(v->zero_digest); 952 + verity_free_sig(v); 988 953 989 954 if (v->ahash_tfm) { 990 955 static_branch_dec(&ahash_enabled); ··· 1455 1418 ti->error = "Root hash verification failed"; 1456 1419 goto bad; 1457 1420 } 1421 + 1422 + r = verity_init_sig(v, verify_args.sig, verify_args.sig_size); 1423 + if (r < 0) { 1424 + ti->error = "Cannot allocate root digest signature"; 1425 + goto bad; 1426 + } 1427 + 1458 1428 v->hash_per_block_bits = 1459 1429 __fls((1 << v->hash_dev_block_bits) / v->digest_size); 1460 1430 ··· 1603 1559 return 0; 1604 1560 } 1605 1561 1562 + #ifdef CONFIG_SECURITY 1563 + 1564 + #ifdef CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG 1565 + 1566 + static int verity_security_set_signature(struct block_device *bdev, 1567 + struct dm_verity *v) 1568 + { 1569 + /* 1570 + * if the dm-verity target is unsigned, v->root_digest_sig will 1571 + * be NULL, and the hook call is still required to let LSMs mark 1572 + * the device as unsigned. This information is crucial for LSMs to 1573 + * block operations such as execution on unsigned files 1574 + */ 1575 + return security_bdev_setintegrity(bdev, 1576 + LSM_INT_DMVERITY_SIG_VALID, 1577 + v->root_digest_sig, 1578 + v->sig_size); 1579 + } 1580 + 1581 + #else 1582 + 1583 + static inline int verity_security_set_signature(struct block_device *bdev, 1584 + struct dm_verity *v) 1585 + { 1586 + return 0; 1587 + } 1588 + 1589 + #endif /* CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG */ 1590 + 1591 + /* 1592 + * Expose verity target's root hash and signature data to LSMs before resume. 1593 + * 1594 + * Returns 0 on success, or -ENOMEM if the system is out of memory. 1595 + */ 1596 + static int verity_preresume(struct dm_target *ti) 1597 + { 1598 + struct block_device *bdev; 1599 + struct dm_verity_digest root_digest; 1600 + struct dm_verity *v; 1601 + int r; 1602 + 1603 + v = ti->private; 1604 + bdev = dm_disk(dm_table_get_md(ti->table))->part0; 1605 + root_digest.digest = v->root_digest; 1606 + root_digest.digest_len = v->digest_size; 1607 + if (static_branch_unlikely(&ahash_enabled) && !v->shash_tfm) 1608 + root_digest.alg = crypto_ahash_alg_name(v->ahash_tfm); 1609 + else 1610 + root_digest.alg = crypto_shash_alg_name(v->shash_tfm); 1611 + 1612 + r = security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, &root_digest, 1613 + sizeof(root_digest)); 1614 + if (r) 1615 + return r; 1616 + 1617 + r = verity_security_set_signature(bdev, v); 1618 + if (r) 1619 + goto bad; 1620 + 1621 + return 0; 1622 + 1623 + bad: 1624 + 1625 + security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, NULL, 0); 1626 + 1627 + return r; 1628 + } 1629 + 1630 + #endif /* CONFIG_SECURITY */ 1631 + 1606 1632 static struct target_type verity_target = { 1607 1633 .name = "verity", 1634 + /* Note: the LSMs depend on the singleton and immutable features */ 1608 1635 .features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE, 1609 1636 .version = {1, 10, 0}, 1610 1637 .module = THIS_MODULE, ··· 1686 1571 .prepare_ioctl = verity_prepare_ioctl, 1687 1572 .iterate_devices = verity_iterate_devices, 1688 1573 .io_hints = verity_io_hints, 1574 + #ifdef CONFIG_SECURITY 1575 + .preresume = verity_preresume, 1576 + #endif /* CONFIG_SECURITY */ 1689 1577 }; 1690 1578 module_dm(verity); 1691 1579
+4
drivers/md/dm-verity.h
··· 45 45 u8 *salt; /* salt: its size is salt_size */ 46 46 u8 *initial_hashstate; /* salted initial state, if shash_tfm is set */ 47 47 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 */ 48 52 unsigned int salt_size; 49 53 sector_t data_start; /* data offset in 512-byte sectors */ 50 54 sector_t hash_start; /* hash start in blocks */
+8 -1
include/linux/security.h
··· 83 83 LSM_POLICY_CHANGE, 84 84 }; 85 85 86 + struct dm_verity_digest { 87 + const char *alg; 88 + const u8 *digest; 89 + size_t digest_len; 90 + }; 91 + 86 92 enum lsm_integrity_type { 87 - __LSM_INT_MAX 93 + LSM_INT_DMVERITY_SIG_VALID, 94 + LSM_INT_DMVERITY_ROOTHASH, 88 95 }; 89 96 90 97 /*