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.

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 <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: merge fuzz, subject line tweaks]
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Deven Bowers and committed by
Paul Moore
b55d26bd a68916ea

+145
+7
block/bdev.c
··· 24 24 #include <linux/pseudo_fs.h> 25 25 #include <linux/uio.h> 26 26 #include <linux/namei.h> 27 + #include <linux/security.h> 27 28 #include <linux/part_stat.h> 28 29 #include <linux/uaccess.h> 29 30 #include <linux/stat.h> ··· 325 324 if (!ei) 326 325 return NULL; 327 326 memset(&ei->bdev, 0, sizeof(ei->bdev)); 327 + 328 + if (security_bdev_alloc(&ei->bdev)) { 329 + kmem_cache_free(bdev_cachep, ei); 330 + return NULL; 331 + } 328 332 return &ei->vfs_inode; 329 333 } 330 334 ··· 339 333 340 334 free_percpu(bdev->bd_stats); 341 335 kfree(bdev->bd_meta_info); 336 + security_bdev_free(bdev); 342 337 343 338 if (!bdev_is_partition(bdev)) { 344 339 if (bdev->bd_disk && bdev->bd_disk->bdi)
+3
include/linux/blk_types.h
··· 71 71 72 72 struct partition_meta_info *bd_meta_info; 73 73 int bd_writers; 74 + #ifdef CONFIG_SECURITY 75 + void *bd_security; 76 + #endif 74 77 /* 75 78 * keep this out-of-line as it's both big and not needed in the fast 76 79 * path
+5
include/linux/lsm_hook_defs.h
··· 451 451 #endif /* CONFIG_IO_URING */ 452 452 453 453 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)
+1
include/linux/lsm_hooks.h
··· 83 83 int lbs_task; 84 84 int lbs_xattr_count; /* number of xattr slots in new_xattrs array */ 85 85 int lbs_tun_dev; 86 + int lbs_bdev; 86 87 }; 87 88 88 89 /*
+26
include/linux/security.h
··· 83 83 LSM_POLICY_CHANGE, 84 84 }; 85 85 86 + enum lsm_integrity_type { 87 + __LSM_INT_MAX 88 + }; 89 + 86 90 /* 87 91 * These are reasons that can be passed to the security_locked_down() 88 92 * LSM hook. Lockdown reasons that protect kernel integrity (ie, the ··· 513 509 int security_locked_down(enum lockdown_reason what); 514 510 int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len, 515 511 void *val, size_t val_len, u64 id, u64 flags); 512 + int security_bdev_alloc(struct block_device *bdev); 513 + void security_bdev_free(struct block_device *bdev); 514 + int security_bdev_setintegrity(struct block_device *bdev, 515 + enum lsm_integrity_type type, const void *value, 516 + size_t size); 516 517 #else /* CONFIG_SECURITY */ 517 518 518 519 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data) ··· 1492 1483 { 1493 1484 return -EOPNOTSUPP; 1494 1485 } 1486 + 1487 + static inline int security_bdev_alloc(struct block_device *bdev) 1488 + { 1489 + return 0; 1490 + } 1491 + 1492 + static inline void security_bdev_free(struct block_device *bdev) 1493 + { 1494 + } 1495 + 1496 + static inline int security_bdev_setintegrity(struct block_device *bdev, 1497 + enum lsm_integrity_type type, 1498 + const void *value, size_t size) 1499 + { 1500 + return 0; 1501 + } 1502 + 1495 1503 #endif /* CONFIG_SECURITY */ 1496 1504 1497 1505 #if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
+103
security/security.c
··· 29 29 #include <linux/msg.h> 30 30 #include <linux/overflow.h> 31 31 #include <linux/perf_event.h> 32 + #include <linux/fs.h> 32 33 #include <net/flow.h> 33 34 #include <net/sock.h> 34 35 ··· 240 239 lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev); 241 240 lsm_set_blob_size(&needed->lbs_xattr_count, 242 241 &blob_sizes.lbs_xattr_count); 242 + lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev); 243 243 } 244 244 245 245 /* Prepare LSM for initialization. */ ··· 421 419 init_debug("task blob size = %d\n", blob_sizes.lbs_task); 422 420 init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev); 423 421 init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); 422 + init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); 424 423 425 424 /* 426 425 * Create any kmem_caches needed for blobs ··· 759 756 { 760 757 return lsm_blob_alloc(&mp->security, blob_sizes.lbs_msg_msg, 761 758 GFP_KERNEL); 759 + } 760 + 761 + /** 762 + * lsm_bdev_alloc - allocate a composite block_device blob 763 + * @bdev: the block_device that needs a blob 764 + * 765 + * Allocate the block_device blob for all the modules 766 + * 767 + * Returns 0, or -ENOMEM if memory can't be allocated. 768 + */ 769 + static int lsm_bdev_alloc(struct block_device *bdev) 770 + { 771 + if (blob_sizes.lbs_bdev == 0) { 772 + bdev->bd_security = NULL; 773 + return 0; 774 + } 775 + 776 + bdev->bd_security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL); 777 + if (!bdev->bd_security) 778 + return -ENOMEM; 779 + 780 + return 0; 762 781 } 763 782 764 783 /** ··· 5682 5657 return call_int_hook(locked_down, what); 5683 5658 } 5684 5659 EXPORT_SYMBOL(security_locked_down); 5660 + 5661 + /** 5662 + * security_bdev_alloc() - Allocate a block device LSM blob 5663 + * @bdev: block device 5664 + * 5665 + * Allocate and attach a security structure to @bdev->bd_security. The 5666 + * security field is initialized to NULL when the bdev structure is 5667 + * allocated. 5668 + * 5669 + * Return: Return 0 if operation was successful. 5670 + */ 5671 + int security_bdev_alloc(struct block_device *bdev) 5672 + { 5673 + int rc = 0; 5674 + 5675 + rc = lsm_bdev_alloc(bdev); 5676 + if (unlikely(rc)) 5677 + return rc; 5678 + 5679 + rc = call_int_hook(bdev_alloc_security, bdev); 5680 + if (unlikely(rc)) 5681 + security_bdev_free(bdev); 5682 + 5683 + return rc; 5684 + } 5685 + EXPORT_SYMBOL(security_bdev_alloc); 5686 + 5687 + /** 5688 + * security_bdev_free() - Free a block device's LSM blob 5689 + * @bdev: block device 5690 + * 5691 + * Deallocate the bdev security structure and set @bdev->bd_security to NULL. 5692 + */ 5693 + void security_bdev_free(struct block_device *bdev) 5694 + { 5695 + if (!bdev->bd_security) 5696 + return; 5697 + 5698 + call_void_hook(bdev_free_security, bdev); 5699 + 5700 + kfree(bdev->bd_security); 5701 + bdev->bd_security = NULL; 5702 + } 5703 + EXPORT_SYMBOL(security_bdev_free); 5704 + 5705 + /** 5706 + * security_bdev_setintegrity() - Set the device's integrity data 5707 + * @bdev: block device 5708 + * @type: type of integrity, e.g. hash digest, signature, etc 5709 + * @value: the integrity value 5710 + * @size: size of the integrity value 5711 + * 5712 + * Register a verified integrity measurement of a bdev with LSMs. 5713 + * LSMs should free the previously saved data if @value is NULL. 5714 + * Please note that the new hook should be invoked every time the security 5715 + * information is updated to keep these data current. For example, in dm-verity, 5716 + * if the mapping table is reloaded and configured to use a different dm-verity 5717 + * target with a new roothash and signing information, the previously stored data 5718 + * in the LSM blob will become obsolete. It is crucial to re-invoke the hook to 5719 + * refresh these data and ensure they are up to date. This necessity arises from 5720 + * the design of device-mapper, where a device-mapper device is first created, and 5721 + * then targets are subsequently loaded into it. These targets can be modified 5722 + * multiple times during the device's lifetime. Therefore, while the LSM blob is 5723 + * allocated during the creation of the block device, its actual contents are 5724 + * not initialized at this stage and can change substantially over time. This 5725 + * includes alterations from data that the LSMs 'trusts' to those they do not, 5726 + * making it essential to handle these changes correctly. Failure to address 5727 + * this dynamic aspect could potentially allow for bypassing LSM checks. 5728 + * 5729 + * Return: Returns 0 on success, negative values on failure. 5730 + */ 5731 + int security_bdev_setintegrity(struct block_device *bdev, 5732 + enum lsm_integrity_type type, const void *value, 5733 + size_t size) 5734 + { 5735 + return call_int_hook(bdev_setintegrity, bdev, type, value, size); 5736 + } 5737 + EXPORT_SYMBOL(security_bdev_setintegrity); 5685 5738 5686 5739 #ifdef CONFIG_PERF_EVENTS 5687 5740 /**