From: Deven Bowers <[email protected]>

dm-verity operates on the block_device level. In order to allow IPE to
determine if a file is sourced from a dm-verity volume, and how that
dm-verity volume was created, create an LSM blob with the signature
data and roothash information, allowing IPE to make decisions about
controls to a resource based on dm-verity information.

Co-developed-by: Fan Wu <[email protected]>
Signed-off-by: Fan Wu <[email protected]>
Signed-off-by: Deven Bowers <[email protected]>
---

Relevant changes since v5:
  * Change if statement condition in security_bdev_setsecurity to be
    more concise, as suggested by Casey Schaufler and Al Viro

Relevant changes since v6:
  * Squash patch 05/12, 07/12, 09/12 to [10/16]

---
 block/bdev.c                      |  7 ++++
 drivers/md/dm-verity-target.c     | 20 ++++++++-
 drivers/md/dm-verity-verify-sig.c | 16 +++++--
 drivers/md/dm-verity-verify-sig.h | 10 +++--
 include/linux/blk_types.h         |  1 +
 include/linux/device-mapper.h     |  3 ++
 include/linux/lsm_hook_defs.h     |  5 +++
 include/linux/lsm_hooks.h         | 12 ++++++
 include/linux/security.h          | 22 ++++++++++
 security/security.c               | 70 +++++++++++++++++++++++++++++++
 10 files changed, 157 insertions(+), 9 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index 485a258b0ab3..4c0d6aaa1f08 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -23,6 +23,7 @@
 #include <linux/pseudo_fs.h>
 #include <linux/uio.h>
 #include <linux/namei.h>
+#include <linux/security.h>
 #include <linux/cleancache.h>
 #include <linux/part_stat.h>
 #include <linux/uaccess.h>
@@ -393,6 +394,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;
 }
 
@@ -402,6 +408,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/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 22a5ac82446a..e62480803e56 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -13,11 +13,14 @@
  * access behavior.
  */
 
+#include "dm-core.h"
 #include "dm-verity.h"
 #include "dm-verity-fec.h"
 #include "dm-verity-verify-sig.h"
+#include "dm-core.h"
 #include <linux/module.h>
 #include <linux/reboot.h>
+#include <linux/security.h>
 
 #define DM_MSG_PREFIX                  "verity"
 
@@ -1051,6 +1054,7 @@ static int verity_ctr(struct dm_target *ti, unsigned 
argc, char **argv)
        sector_t hash_position;
        char dummy;
        char *root_hash_digest_to_validate;
+       struct block_device *bdev;
 
        v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
        if (!v) {
@@ -1084,6 +1088,13 @@ static int verity_ctr(struct dm_target *ti, unsigned 
argc, char **argv)
        }
        v->version = num;
 
+       bdev = dm_table_get_md(ti->table)->disk->part0;
+       if (!bdev) {
+               ti->error = "Mapped device lookup failed";
+               r = -ENOMEM;
+               goto bad;
+       }
+
        r = dm_get_device(ti, argv[1], FMODE_READ, &v->data_dev);
        if (r) {
                ti->error = "Data device lookup failed";
@@ -1216,7 +1227,7 @@ static int verity_ctr(struct dm_target *ti, unsigned 
argc, char **argv)
        }
 
        /* Root hash signature is  a optional parameter*/
-       r = verity_verify_root_hash(root_hash_digest_to_validate,
+       r = verity_verify_root_hash(bdev, root_hash_digest_to_validate,
                                    strlen(root_hash_digest_to_validate),
                                    verify_args.sig,
                                    verify_args.sig_size);
@@ -1289,12 +1300,17 @@ static int verity_ctr(struct dm_target *ti, unsigned 
argc, char **argv)
        ti->per_io_data_size = roundup(ti->per_io_data_size,
                                       __alignof__(struct dm_verity_io));
 
+       r = security_bdev_setsecurity(bdev,
+                                     DM_VERITY_ROOTHASH_SEC_NAME,
+                                     v->root_digest, v->digest_size);
+       if (r)
+               goto bad;
+
        verity_verify_sig_opts_cleanup(&verify_args);
 
        return 0;
 
 bad:
-
        verity_verify_sig_opts_cleanup(&verify_args);
        verity_dtr(ti);
 
diff --git a/drivers/md/dm-verity-verify-sig.c 
b/drivers/md/dm-verity-verify-sig.c
index db61a1f43ae9..1672a35f292b 100644
--- a/drivers/md/dm-verity-verify-sig.c
+++ b/drivers/md/dm-verity-verify-sig.c
@@ -8,7 +8,10 @@
 #include <linux/device-mapper.h>
 #include <linux/verification.h>
 #include <keys/user-type.h>
+#include <linux/security.h>
+#include <linux/list.h>
 #include <linux/module.h>
+#include "dm-core.h"
 #include "dm-verity.h"
 #include "dm-verity-verify-sig.h"
 
@@ -97,14 +100,17 @@ int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
  * verify_verify_roothash - Verify the root hash of the verity hash device
  *                          using builtin trusted keys.
  *
+ * @bdev: block_device representing the device-mapper created block device.
+ *     Used by the security hook, to set information about the block_device.
  * @root_hash: For verity, the roothash/data to be verified.
  * @root_hash_len: Size of the roothash/data to be verified.
  * @sig_data: The trusted signature that verifies the roothash/data.
  * @sig_len: Size of the signature.
  *
  */
-int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
-                           const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct block_device *bdev, const void *root_hash,
+                           size_t root_hash_len, const void *sig_data,
+                           size_t sig_len)
 {
        int ret;
 
@@ -126,8 +132,12 @@ int verity_verify_root_hash(const void *root_hash, size_t 
root_hash_len,
                                NULL,
 #endif
                                VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
+       if (ret)
+               return ret;
 
-       return ret;
+       return security_bdev_setsecurity(bdev,
+                                        DM_VERITY_SIGNATURE_SEC_NAME,
+                                        sig_data, sig_len);
 }
 
 void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
diff --git a/drivers/md/dm-verity-verify-sig.h 
b/drivers/md/dm-verity-verify-sig.h
index 3987c7141f79..31692fff92e4 100644
--- a/drivers/md/dm-verity-verify-sig.h
+++ b/drivers/md/dm-verity-verify-sig.h
@@ -20,8 +20,9 @@ struct dm_verity_sig_opts {
 
 #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 2
 
-int verity_verify_root_hash(const void *data, size_t data_len,
-                           const void *sig_data, size_t sig_len);
+int verity_verify_root_hash(struct block_device *bdev, const void *data,
+                           size_t data_len, const void *sig_data,
+                           size_t sig_len);
 bool verity_verify_is_sig_opt_arg(const char *arg_name);
 
 int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity 
*v,
@@ -34,8 +35,9 @@ void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts 
*sig_opts);
 
 #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 0
 
-static inline int verity_verify_root_hash(const void *data, size_t data_len,
-                                         const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct block_device *bdev, const void *data,
+                           size_t data_len, const void *sig_data,
+                           size_t sig_len)
 {
        return 0;
 }
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index be622b5a21ed..58def70aa653 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -49,6 +49,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/device-mapper.h b/include/linux/device-mapper.h
index 114553b487ef..0f5bdcfcf337 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -665,4 +665,7 @@ static inline unsigned long to_bytes(sector_t n)
        return (n << SECTOR_SHIFT);
 }
 
+#define DM_VERITY_SIGNATURE_SEC_NAME DM_NAME   ".verity-sig"
+#define DM_VERITY_ROOTHASH_SEC_NAME DM_NAME    ".verity-rh"
+
 #endif /* _LINUX_DEVICE_MAPPER_H */
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 2adeea44c0d5..b148a01b2cef 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -402,3 +402,8 @@ LSM_HOOK(void, LSM_RET_VOID, perf_event_free, struct 
perf_event *event)
 LSM_HOOK(int, 0, perf_event_read, struct perf_event *event)
 LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
 #endif /* CONFIG_PERF_EVENTS */
+
+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 5c4c5c0602cb..43d357e6ab47 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1545,6 +1545,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 -ENOSYS.
+ *
  * Security hooks for perf events
  *
  * @perf_event_open:
@@ -1592,6 +1603,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 5b7288521300..98af3f645cb6 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -472,6 +472,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)
@@ -1348,6 +1353,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 9ffa9e9c5c55..d7ac9f01500b 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
 
@@ -206,6 +207,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. */
@@ -342,6 +344,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
@@ -659,6 +662,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
@@ -2599,6 +2624,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 0;
+}
+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 != -ENOSYS)
+                       return rc;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(security_bdev_setsecurity);
+
 #ifdef CONFIG_PERF_EVENTS
 int security_perf_event_open(struct perf_event_attr *attr, int type)
 {
-- 
2.33.0

--
Linux-audit mailing list
[email protected]
https://listman.redhat.com/mailman/listinfo/linux-audit

Reply via email to