From: Deven Bowers <deven.desai(a)linux.microsoft.com>
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 <wufan(a)linux.microsoft.com>
Signed-off-by: Fan Wu <wufan(a)linux.microsoft.com>
Signed-off-by: Deven Bowers <deven.desai(a)linux.microsoft.com>
---
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