diff mbox series

[RFC,v8,10/17] block|security: add LSM blob to block_device

Message ID 1654714889-26728-11-git-send-email-deven.desai@linux.microsoft.com (mailing list archive)
State Not Applicable
Headers show
Series Integrity Policy Enforcement LSM (IPE) | expand

Commit Message

Deven Bowers June 8, 2022, 7:01 p.m. UTC
block_device structures can have valuable security properties,
based on how they are created, and what subsystem manages them.

By adding LSM storage to this structure, this data can be accessed
at the LSM layer.

Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>

---
v2:
  + No Changes

v3:
  + Minor style changes from checkpatch --strict

v4:
  + No Changes

v5:
  + Allow multiple callers to call security_bdev_setsecurity

v6:
  + Simplify security_bdev_setsecurity break condition

v7:
  + Squash all dm-verity related patches to two patches,
    the additions to dm-verity/fs, and the consumption of
    the additions.

v8:
  + Split dm-verity related patches squashed in v7 to 3 commits based on
    topic:
      + New LSM hook
      + Consumption of hook outside LSM
      + Consumption of hook inside LSM.

  + change return of security_bdev_alloc / security_bdev_setsecurity
    to LSM_RET_DEFAULT instead of 0.

  + Change return code to -EOPNOTSUPP, bring inline with other
    setsecurity hooks.
---
 block/bdev.c                  |  7 ++++
 include/linux/blk_types.h     |  1 +
 include/linux/lsm_hook_defs.h |  5 +++
 include/linux/lsm_hooks.h     | 12 ++++++
 include/linux/security.h      | 22 +++++++++++
 security/security.c           | 70 +++++++++++++++++++++++++++++++++++
 6 files changed, 117 insertions(+)

Comments

Casey Schaufler June 8, 2022, 8:07 p.m. UTC | #1
On 6/8/2022 12:01 PM, Deven Bowers wrote:
> block_device structures can have valuable security properties,
> based on how they are created, and what subsystem manages them.
>
> By adding LSM storage to this structure, this data can be accessed
> at the LSM layer.
>
> Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>

Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>

Not everyone is going to appreciate the infrastructure allocation
of the block_device security blob, but I do.

>
> ---
> v2:
>    + No Changes
>
> v3:
>    + Minor style changes from checkpatch --strict
>
> v4:
>    + No Changes
>
> v5:
>    + Allow multiple callers to call security_bdev_setsecurity
>
> v6:
>    + Simplify security_bdev_setsecurity break condition
>
> v7:
>    + Squash all dm-verity related patches to two patches,
>      the additions to dm-verity/fs, and the consumption of
>      the additions.
>
> v8:
>    + Split dm-verity related patches squashed in v7 to 3 commits based on
>      topic:
>        + New LSM hook
>        + Consumption of hook outside LSM
>        + Consumption of hook inside LSM.
>
>    + change return of security_bdev_alloc / security_bdev_setsecurity
>      to LSM_RET_DEFAULT instead of 0.
>
>    + Change return code to -EOPNOTSUPP, bring inline with other
>      setsecurity hooks.
> ---
>   block/bdev.c                  |  7 ++++
>   include/linux/blk_types.h     |  1 +
>   include/linux/lsm_hook_defs.h |  5 +++
>   include/linux/lsm_hooks.h     | 12 ++++++
>   include/linux/security.h      | 22 +++++++++++
>   security/security.c           | 70 +++++++++++++++++++++++++++++++++++
>   6 files changed, 117 insertions(+)
>
> diff --git a/block/bdev.c b/block/bdev.c
> index 5fe06c1f2def..e7ef2c7a22c9 100644
> --- a/block/bdev.c
> +++ b/block/bdev.c
> @@ -24,6 +24,7 @@
>   #include <linux/pseudo_fs.h>
>   #include <linux/uio.h>
>   #include <linux/namei.h>
> +#include <linux/security.h>
>   #include <linux/part_stat.h>
>   #include <linux/uaccess.h>
>   #include "../fs/internal.h"
> @@ -397,6 +398,11 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
>   	if (!ei)
>   		return NULL;
>   	memset(&ei->bdev, 0, sizeof(ei->bdev));
> +
> +	if (unlikely(security_bdev_alloc(&ei->bdev))) {
> +		kmem_cache_free(bdev_cachep, ei);
> +		return NULL;
> +	}
>   	return &ei->vfs_inode;
>   }
>   
> @@ -406,6 +412,7 @@ static void bdev_free_inode(struct inode *inode)
>   
>   	free_percpu(bdev->bd_stats);
>   	kfree(bdev->bd_meta_info);
> +	security_bdev_free(bdev);
>   
>   	if (!bdev_is_partition(bdev)) {
>   		if (bdev->bd_disk && bdev->bd_disk->bdi)
> diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
> index a24d4078fb21..a014ffa14b2d 100644
> --- a/include/linux/blk_types.h
> +++ b/include/linux/blk_types.h
> @@ -68,6 +68,7 @@ struct block_device {
>   #ifdef CONFIG_FAIL_MAKE_REQUEST
>   	bool			bd_make_it_fail;
>   #endif
> +	void			*security;
>   } __randomize_layout;
>   
>   #define bdev_whole(_bdev) \
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index eafa1d2489fd..3449c004bd84 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -407,3 +407,8 @@ LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
>   LSM_HOOK(int, 0, uring_override_creds, const struct cred *new)
>   LSM_HOOK(int, 0, uring_sqpoll, void)
>   #endif /* CONFIG_IO_URING */
> +
> +LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
> +LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
> +LSM_HOOK(int, 0, bdev_setsecurity, struct block_device *bdev, const char *name,
> +	 const void *value, size_t size)
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 91c8146649f5..9f011d705ea8 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1550,6 +1550,17 @@
>    *
>    *     @what: kernel feature being accessed
>    *
> + * @bdev_alloc_security:
> + *	Initialize the security field inside a block_device structure.
> + *
> + * @bdev_free_security:
> + *	Cleanup the security information stored inside a block_device structure.
> + *
> + * @bdev_setsecurity:
> + *	Set a security property associated with @name for @bdev with
> + *	value @value. @size indicates the size of @value in bytes.
> + *	If a @name is not implemented, return -EOPNOTSUPP.
> + *
>    * Security hooks for perf events
>    *
>    * @perf_event_open:
> @@ -1610,6 +1621,7 @@ struct lsm_blob_sizes {
>   	int	lbs_ipc;
>   	int	lbs_msg_msg;
>   	int	lbs_task;
> +	int	lbs_bdev;
>   };
>   
>   /*
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 7fc4e9f49f54..30b663de301f 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -473,6 +473,11 @@ int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
>   int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
>   int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
>   int security_locked_down(enum lockdown_reason what);
> +int security_bdev_alloc(struct block_device *bdev);
> +void security_bdev_free(struct block_device *bdev);
> +int security_bdev_setsecurity(struct block_device *bdev,
> +			      const char *name, const void *value,
> +			      size_t size);
>   #else /* CONFIG_SECURITY */
>   
>   static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
> @@ -1349,6 +1354,23 @@ static inline int security_locked_down(enum lockdown_reason what)
>   {
>   	return 0;
>   }
> +
> +static inline int security_bdev_alloc(struct block_device *bdev)
> +{
> +	return 0;
> +}
> +
> +static inline void security_bdev_free(struct block_device *bdev)
> +{
> +}
> +
> +static inline int security_bdev_setsecurity(struct block_device *bdev,
> +					    const char *name,
> +					    const void *value, size_t size)
> +{
> +	return 0;
> +}
> +
>   #endif	/* CONFIG_SECURITY */
>   
>   #if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
> diff --git a/security/security.c b/security/security.c
> index 188b8f782220..59ec336788e8 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -29,6 +29,7 @@
>   #include <linux/string.h>
>   #include <linux/msg.h>
>   #include <net/flow.h>
> +#include <linux/fs.h>
>   
>   #define MAX_LSM_EVM_XATTR	2
>   
> @@ -208,6 +209,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
>   	lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
>   	lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
>   	lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> +	lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
>   }
>   
>   /* Prepare LSM for initialization. */
> @@ -344,6 +346,7 @@ static void __init ordered_lsm_init(void)
>   	init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
>   	init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
>   	init_debug("task blob size       = %d\n", blob_sizes.lbs_task);
> +	init_debug("bdev blob size       = %d\n", blob_sizes.lbs_bdev);
>   
>   	/*
>   	 * Create any kmem_caches needed for blobs
> @@ -660,6 +663,28 @@ static int lsm_msg_msg_alloc(struct msg_msg *mp)
>   	return 0;
>   }
>   
> +/**
> + * lsm_bdev_alloc - allocate a composite block_device blob
> + * @bdev: the block_device that needs a blob
> + *
> + * Allocate the block_device blob for all the modules
> + *
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> + */
> +static int lsm_bdev_alloc(struct block_device *bdev)
> +{
> +	if (blob_sizes.lbs_bdev == 0) {
> +		bdev->security = NULL;
> +		return 0;
> +	}
> +
> +	bdev->security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
> +	if (!bdev->security)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
>   /**
>    * lsm_early_task - during initialization allocate a composite task blob
>    * @task: the task that needs a blob
> @@ -2617,6 +2642,51 @@ int security_locked_down(enum lockdown_reason what)
>   }
>   EXPORT_SYMBOL(security_locked_down);
>   
> +int security_bdev_alloc(struct block_device *bdev)
> +{
> +	int rc = 0;
> +
> +	rc = lsm_bdev_alloc(bdev);
> +	if (unlikely(rc))
> +		return rc;
> +
> +	rc = call_int_hook(bdev_alloc_security, 0, bdev);
> +	if (unlikely(rc))
> +		security_bdev_free(bdev);
> +
> +	return LSM_RET_DEFAULT(bdev_alloc_security);
> +}
> +EXPORT_SYMBOL(security_bdev_alloc);
> +
> +void security_bdev_free(struct block_device *bdev)
> +{
> +	if (!bdev->security)
> +		return;
> +
> +	call_void_hook(bdev_free_security, bdev);
> +
> +	kfree(bdev->security);
> +	bdev->security = NULL;
> +}
> +EXPORT_SYMBOL(security_bdev_free);
> +
> +int security_bdev_setsecurity(struct block_device *bdev,
> +			      const char *name, const void *value,
> +			      size_t size)
> +{
> +	int rc = 0;
> +	struct security_hook_list *p;
> +
> +	hlist_for_each_entry(p, &security_hook_heads.bdev_setsecurity, list) {
> +		rc = p->hook.bdev_setsecurity(bdev, name, value, size);
> +		if (rc && rc != -EOPNOTSUPP)
> +			return rc;
> +	}
> +
> +	return LSM_RET_DEFAULT(bdev_setsecurity);
> +}
> +EXPORT_SYMBOL(security_bdev_setsecurity);
> +
>   #ifdef CONFIG_PERF_EVENTS
>   int security_perf_event_open(struct perf_event_attr *attr, int type)
>   {
Deven Bowers June 8, 2022, 10:28 p.m. UTC | #2
On Wed, Jun 08, 2022 at 01:07:39PM -0700, Casey Schaufler wrote:
> On 6/8/2022 12:01 PM, Deven Bowers wrote:
> >block_device structures can have valuable security properties,
> >based on how they are created, and what subsystem manages them.
> >
> >By adding LSM storage to this structure, this data can be accessed
> >at the LSM layer.
> >
> >Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
> 
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> 
> Not everyone is going to appreciate the infrastructure allocation
> of the block_device security blob, but I do.

Thanks Casey.
diff mbox series

Patch

diff --git a/block/bdev.c b/block/bdev.c
index 5fe06c1f2def..e7ef2c7a22c9 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -24,6 +24,7 @@ 
 #include <linux/pseudo_fs.h>
 #include <linux/uio.h>
 #include <linux/namei.h>
+#include <linux/security.h>
 #include <linux/part_stat.h>
 #include <linux/uaccess.h>
 #include "../fs/internal.h"
@@ -397,6 +398,11 @@  static struct inode *bdev_alloc_inode(struct super_block *sb)
 	if (!ei)
 		return NULL;
 	memset(&ei->bdev, 0, sizeof(ei->bdev));
+
+	if (unlikely(security_bdev_alloc(&ei->bdev))) {
+		kmem_cache_free(bdev_cachep, ei);
+		return NULL;
+	}
 	return &ei->vfs_inode;
 }
 
@@ -406,6 +412,7 @@  static void bdev_free_inode(struct inode *inode)
 
 	free_percpu(bdev->bd_stats);
 	kfree(bdev->bd_meta_info);
+	security_bdev_free(bdev);
 
 	if (!bdev_is_partition(bdev)) {
 		if (bdev->bd_disk && bdev->bd_disk->bdi)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index a24d4078fb21..a014ffa14b2d 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -68,6 +68,7 @@  struct block_device {
 #ifdef CONFIG_FAIL_MAKE_REQUEST
 	bool			bd_make_it_fail;
 #endif
+	void			*security;
 } __randomize_layout;
 
 #define bdev_whole(_bdev) \
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index eafa1d2489fd..3449c004bd84 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -407,3 +407,8 @@  LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
 LSM_HOOK(int, 0, uring_override_creds, const struct cred *new)
 LSM_HOOK(int, 0, uring_sqpoll, void)
 #endif /* CONFIG_IO_URING */
+
+LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
+LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
+LSM_HOOK(int, 0, bdev_setsecurity, struct block_device *bdev, const char *name,
+	 const void *value, size_t size)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 91c8146649f5..9f011d705ea8 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1550,6 +1550,17 @@ 
  *
  *     @what: kernel feature being accessed
  *
+ * @bdev_alloc_security:
+ *	Initialize the security field inside a block_device structure.
+ *
+ * @bdev_free_security:
+ *	Cleanup the security information stored inside a block_device structure.
+ *
+ * @bdev_setsecurity:
+ *	Set a security property associated with @name for @bdev with
+ *	value @value. @size indicates the size of @value in bytes.
+ *	If a @name is not implemented, return -EOPNOTSUPP.
+ *
  * Security hooks for perf events
  *
  * @perf_event_open:
@@ -1610,6 +1621,7 @@  struct lsm_blob_sizes {
 	int	lbs_ipc;
 	int	lbs_msg_msg;
 	int	lbs_task;
+	int	lbs_bdev;
 };
 
 /*
diff --git a/include/linux/security.h b/include/linux/security.h
index 7fc4e9f49f54..30b663de301f 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -473,6 +473,11 @@  int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
 int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
 int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
 int security_locked_down(enum lockdown_reason what);
+int security_bdev_alloc(struct block_device *bdev);
+void security_bdev_free(struct block_device *bdev);
+int security_bdev_setsecurity(struct block_device *bdev,
+			      const char *name, const void *value,
+			      size_t size);
 #else /* CONFIG_SECURITY */
 
 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -1349,6 +1354,23 @@  static inline int security_locked_down(enum lockdown_reason what)
 {
 	return 0;
 }
+
+static inline int security_bdev_alloc(struct block_device *bdev)
+{
+	return 0;
+}
+
+static inline void security_bdev_free(struct block_device *bdev)
+{
+}
+
+static inline int security_bdev_setsecurity(struct block_device *bdev,
+					    const char *name,
+					    const void *value, size_t size)
+{
+	return 0;
+}
+
 #endif	/* CONFIG_SECURITY */
 
 #if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
diff --git a/security/security.c b/security/security.c
index 188b8f782220..59ec336788e8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -29,6 +29,7 @@ 
 #include <linux/string.h>
 #include <linux/msg.h>
 #include <net/flow.h>
+#include <linux/fs.h>
 
 #define MAX_LSM_EVM_XATTR	2
 
@@ -208,6 +209,7 @@  static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
 	lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
 	lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
 	lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+	lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
 }
 
 /* Prepare LSM for initialization. */
@@ -344,6 +346,7 @@  static void __init ordered_lsm_init(void)
 	init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
 	init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
 	init_debug("task blob size       = %d\n", blob_sizes.lbs_task);
+	init_debug("bdev blob size       = %d\n", blob_sizes.lbs_bdev);
 
 	/*
 	 * Create any kmem_caches needed for blobs
@@ -660,6 +663,28 @@  static int lsm_msg_msg_alloc(struct msg_msg *mp)
 	return 0;
 }
 
+/**
+ * lsm_bdev_alloc - allocate a composite block_device blob
+ * @bdev: the block_device that needs a blob
+ *
+ * Allocate the block_device blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_bdev_alloc(struct block_device *bdev)
+{
+	if (blob_sizes.lbs_bdev == 0) {
+		bdev->security = NULL;
+		return 0;
+	}
+
+	bdev->security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
+	if (!bdev->security)
+		return -ENOMEM;
+
+	return 0;
+}
+
 /**
  * lsm_early_task - during initialization allocate a composite task blob
  * @task: the task that needs a blob
@@ -2617,6 +2642,51 @@  int security_locked_down(enum lockdown_reason what)
 }
 EXPORT_SYMBOL(security_locked_down);
 
+int security_bdev_alloc(struct block_device *bdev)
+{
+	int rc = 0;
+
+	rc = lsm_bdev_alloc(bdev);
+	if (unlikely(rc))
+		return rc;
+
+	rc = call_int_hook(bdev_alloc_security, 0, bdev);
+	if (unlikely(rc))
+		security_bdev_free(bdev);
+
+	return LSM_RET_DEFAULT(bdev_alloc_security);
+}
+EXPORT_SYMBOL(security_bdev_alloc);
+
+void security_bdev_free(struct block_device *bdev)
+{
+	if (!bdev->security)
+		return;
+
+	call_void_hook(bdev_free_security, bdev);
+
+	kfree(bdev->security);
+	bdev->security = NULL;
+}
+EXPORT_SYMBOL(security_bdev_free);
+
+int security_bdev_setsecurity(struct block_device *bdev,
+			      const char *name, const void *value,
+			      size_t size)
+{
+	int rc = 0;
+	struct security_hook_list *p;
+
+	hlist_for_each_entry(p, &security_hook_heads.bdev_setsecurity, list) {
+		rc = p->hook.bdev_setsecurity(bdev, name, value, size);
+		if (rc && rc != -EOPNOTSUPP)
+			return rc;
+	}
+
+	return LSM_RET_DEFAULT(bdev_setsecurity);
+}
+EXPORT_SYMBOL(security_bdev_setsecurity);
+
 #ifdef CONFIG_PERF_EVENTS
 int security_perf_event_open(struct perf_event_attr *attr, int type)
 {