Skip to content

Commit b55d26b

Browse files
bowerscd-corppcmoore
authored andcommitted
block,lsm: add LSM blob and new LSM hooks for block devices
This patch introduces a new LSM blob to the block_device structure, enabling the security subsystem to store security-sensitive data related to block devices. Currently, for a device mapper's mapped device containing a dm-verity target, critical security information such as the roothash and its signing state are not readily accessible. Specifically, while the dm-verity volume creation process passes the dm-verity roothash and its signature from userspace to the kernel, the roothash is stored privately within the dm-verity target, and its signature is discarded post-verification. This makes it extremely hard for the security subsystem to utilize these data. With the addition of the LSM blob to the block_device structure, the security subsystem can now retain and manage important security metadata such as the roothash and the signing state of a dm-verity by storing them inside the blob. Access decisions can then be based on these stored data. The implementation follows the same approach used for security blobs in other structures like struct file, struct inode, and struct superblock. The initialization of the security blob occurs after the creation of the struct block_device, performed by the security subsystem. Similarly, the security blob is freed by the security subsystem before the struct block_device is deallocated or freed. This patch also introduces a new hook security_bdev_setintegrity() to save block device's integrity data to the new LSM blob. For example, for dm-verity, it can use this hook to expose its roothash and signing state to LSMs, then LSMs can save these data into the LSM blob. Please note that the new hook should be invoked every time the security information is updated to keep these data current. For example, in dm-verity, if the mapping table is reloaded and configured to use a different dm-verity target with a new roothash and signing information, the previously stored data in the LSM blob will become obsolete. It is crucial to re-invoke the hook to refresh these data and ensure they are up to date. This necessity arises from the design of device-mapper, where a device-mapper device is first created, and then targets are subsequently loaded into it. These targets can be modified multiple times during the device's lifetime. Therefore, while the LSM blob is allocated during the creation of the block device, its actual contents are not initialized at this stage and can change substantially over time. This includes alterations from data that the LSM 'trusts' to those it does not, making it essential to handle these changes correctly. Failure to address this dynamic aspect could potentially allow for bypassing LSM checks. Signed-off-by: Deven Bowers <[email protected]> Signed-off-by: Fan Wu <[email protected]> [PM: merge fuzz, subject line tweaks] Signed-off-by: Paul Moore <[email protected]>
1 parent a68916e commit b55d26b

File tree

6 files changed

+145
-0
lines changed

6 files changed

+145
-0
lines changed

block/bdev.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/pseudo_fs.h>
2525
#include <linux/uio.h>
2626
#include <linux/namei.h>
27+
#include <linux/security.h>
2728
#include <linux/part_stat.h>
2829
#include <linux/uaccess.h>
2930
#include <linux/stat.h>
@@ -324,6 +325,11 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
324325
if (!ei)
325326
return NULL;
326327
memset(&ei->bdev, 0, sizeof(ei->bdev));
328+
329+
if (security_bdev_alloc(&ei->bdev)) {
330+
kmem_cache_free(bdev_cachep, ei);
331+
return NULL;
332+
}
327333
return &ei->vfs_inode;
328334
}
329335

@@ -333,6 +339,7 @@ static void bdev_free_inode(struct inode *inode)
333339

334340
free_percpu(bdev->bd_stats);
335341
kfree(bdev->bd_meta_info);
342+
security_bdev_free(bdev);
336343

337344
if (!bdev_is_partition(bdev)) {
338345
if (bdev->bd_disk && bdev->bd_disk->bdi)

include/linux/blk_types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ struct block_device {
7171

7272
struct partition_meta_info *bd_meta_info;
7373
int bd_writers;
74+
#ifdef CONFIG_SECURITY
75+
void *bd_security;
76+
#endif
7477
/*
7578
* keep this out-of-line as it's both big and not needed in the fast
7679
* path

include/linux/lsm_hook_defs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,8 @@ LSM_HOOK(int, 0, uring_cmd, struct io_uring_cmd *ioucmd)
451451
#endif /* CONFIG_IO_URING */
452452

453453
LSM_HOOK(void, LSM_RET_VOID, initramfs_populated, void)
454+
455+
LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
456+
LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
457+
LSM_HOOK(int, 0, bdev_setintegrity, struct block_device *bdev,
458+
enum lsm_integrity_type type, const void *value, size_t size)

include/linux/lsm_hooks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct lsm_blob_sizes {
8383
int lbs_task;
8484
int lbs_xattr_count; /* number of xattr slots in new_xattrs array */
8585
int lbs_tun_dev;
86+
int lbs_bdev;
8687
};
8788

8889
/*

include/linux/security.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ enum lsm_event {
8383
LSM_POLICY_CHANGE,
8484
};
8585

86+
enum lsm_integrity_type {
87+
__LSM_INT_MAX
88+
};
89+
8690
/*
8791
* These are reasons that can be passed to the security_locked_down()
8892
* LSM hook. Lockdown reasons that protect kernel integrity (ie, the
@@ -509,6 +513,11 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
509513
int security_locked_down(enum lockdown_reason what);
510514
int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
511515
void *val, size_t val_len, u64 id, u64 flags);
516+
int security_bdev_alloc(struct block_device *bdev);
517+
void security_bdev_free(struct block_device *bdev);
518+
int security_bdev_setintegrity(struct block_device *bdev,
519+
enum lsm_integrity_type type, const void *value,
520+
size_t size);
512521
#else /* CONFIG_SECURITY */
513522

514523
static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -1483,6 +1492,23 @@ static inline int lsm_fill_user_ctx(struct lsm_ctx __user *uctx,
14831492
{
14841493
return -EOPNOTSUPP;
14851494
}
1495+
1496+
static inline int security_bdev_alloc(struct block_device *bdev)
1497+
{
1498+
return 0;
1499+
}
1500+
1501+
static inline void security_bdev_free(struct block_device *bdev)
1502+
{
1503+
}
1504+
1505+
static inline int security_bdev_setintegrity(struct block_device *bdev,
1506+
enum lsm_integrity_type type,
1507+
const void *value, size_t size)
1508+
{
1509+
return 0;
1510+
}
1511+
14861512
#endif /* CONFIG_SECURITY */
14871513

14881514
#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)

security/security.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/msg.h>
3030
#include <linux/overflow.h>
3131
#include <linux/perf_event.h>
32+
#include <linux/fs.h>
3233
#include <net/flow.h>
3334
#include <net/sock.h>
3435

@@ -239,6 +240,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
239240
lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
240241
lsm_set_blob_size(&needed->lbs_xattr_count,
241242
&blob_sizes.lbs_xattr_count);
243+
lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
242244
}
243245

244246
/* Prepare LSM for initialization. */
@@ -419,6 +421,7 @@ static void __init ordered_lsm_init(void)
419421
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
420422
init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
421423
init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
424+
init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
422425

423426
/*
424427
* Create any kmem_caches needed for blobs
@@ -758,6 +761,28 @@ static int lsm_msg_msg_alloc(struct msg_msg *mp)
758761
GFP_KERNEL);
759762
}
760763

764+
/**
765+
* lsm_bdev_alloc - allocate a composite block_device blob
766+
* @bdev: the block_device that needs a blob
767+
*
768+
* Allocate the block_device blob for all the modules
769+
*
770+
* Returns 0, or -ENOMEM if memory can't be allocated.
771+
*/
772+
static int lsm_bdev_alloc(struct block_device *bdev)
773+
{
774+
if (blob_sizes.lbs_bdev == 0) {
775+
bdev->bd_security = NULL;
776+
return 0;
777+
}
778+
779+
bdev->bd_security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
780+
if (!bdev->bd_security)
781+
return -ENOMEM;
782+
783+
return 0;
784+
}
785+
761786
/**
762787
* lsm_early_task - during initialization allocate a composite task blob
763788
* @task: the task that needs a blob
@@ -5658,6 +5683,84 @@ int security_locked_down(enum lockdown_reason what)
56585683
}
56595684
EXPORT_SYMBOL(security_locked_down);
56605685

5686+
/**
5687+
* security_bdev_alloc() - Allocate a block device LSM blob
5688+
* @bdev: block device
5689+
*
5690+
* Allocate and attach a security structure to @bdev->bd_security. The
5691+
* security field is initialized to NULL when the bdev structure is
5692+
* allocated.
5693+
*
5694+
* Return: Return 0 if operation was successful.
5695+
*/
5696+
int security_bdev_alloc(struct block_device *bdev)
5697+
{
5698+
int rc = 0;
5699+
5700+
rc = lsm_bdev_alloc(bdev);
5701+
if (unlikely(rc))
5702+
return rc;
5703+
5704+
rc = call_int_hook(bdev_alloc_security, bdev);
5705+
if (unlikely(rc))
5706+
security_bdev_free(bdev);
5707+
5708+
return rc;
5709+
}
5710+
EXPORT_SYMBOL(security_bdev_alloc);
5711+
5712+
/**
5713+
* security_bdev_free() - Free a block device's LSM blob
5714+
* @bdev: block device
5715+
*
5716+
* Deallocate the bdev security structure and set @bdev->bd_security to NULL.
5717+
*/
5718+
void security_bdev_free(struct block_device *bdev)
5719+
{
5720+
if (!bdev->bd_security)
5721+
return;
5722+
5723+
call_void_hook(bdev_free_security, bdev);
5724+
5725+
kfree(bdev->bd_security);
5726+
bdev->bd_security = NULL;
5727+
}
5728+
EXPORT_SYMBOL(security_bdev_free);
5729+
5730+
/**
5731+
* security_bdev_setintegrity() - Set the device's integrity data
5732+
* @bdev: block device
5733+
* @type: type of integrity, e.g. hash digest, signature, etc
5734+
* @value: the integrity value
5735+
* @size: size of the integrity value
5736+
*
5737+
* Register a verified integrity measurement of a bdev with LSMs.
5738+
* LSMs should free the previously saved data if @value is NULL.
5739+
* Please note that the new hook should be invoked every time the security
5740+
* information is updated to keep these data current. For example, in dm-verity,
5741+
* if the mapping table is reloaded and configured to use a different dm-verity
5742+
* target with a new roothash and signing information, the previously stored data
5743+
* in the LSM blob will become obsolete. It is crucial to re-invoke the hook to
5744+
* refresh these data and ensure they are up to date. This necessity arises from
5745+
* the design of device-mapper, where a device-mapper device is first created, and
5746+
* then targets are subsequently loaded into it. These targets can be modified
5747+
* multiple times during the device's lifetime. Therefore, while the LSM blob is
5748+
* allocated during the creation of the block device, its actual contents are
5749+
* not initialized at this stage and can change substantially over time. This
5750+
* includes alterations from data that the LSMs 'trusts' to those they do not,
5751+
* making it essential to handle these changes correctly. Failure to address
5752+
* this dynamic aspect could potentially allow for bypassing LSM checks.
5753+
*
5754+
* Return: Returns 0 on success, negative values on failure.
5755+
*/
5756+
int security_bdev_setintegrity(struct block_device *bdev,
5757+
enum lsm_integrity_type type, const void *value,
5758+
size_t size)
5759+
{
5760+
return call_int_hook(bdev_setintegrity, bdev, type, value, size);
5761+
}
5762+
EXPORT_SYMBOL(security_bdev_setintegrity);
5763+
56615764
#ifdef CONFIG_PERF_EVENTS
56625765
/**
56635766
* security_perf_event_open() - Check if a perf event open is allowed

0 commit comments

Comments
 (0)