diff options
Diffstat (limited to 'drivers/md/dm-verity-target.c')
-rw-r--r-- | drivers/md/dm-verity-target.c | 141 |
1 files changed, 139 insertions, 2 deletions
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index cf659c8feb29..36e4ddfe2d15 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -22,6 +22,7 @@ #include <linux/scatterlist.h> #include <linux/string.h> #include <linux/jump_label.h> +#include <linux/security.h> #define DM_MSG_PREFIX "verity" @@ -272,8 +273,10 @@ out: if (v->mode == DM_VERITY_MODE_LOGGING) return 0; - if (v->mode == DM_VERITY_MODE_RESTART) - kernel_restart("dm-verity device corrupted"); + if (v->mode == DM_VERITY_MODE_RESTART) { + pr_emerg("dm-verity device corrupted\n"); + emergency_restart(); + } if (v->mode == DM_VERITY_MODE_PANIC) panic("dm-verity device corrupted"); @@ -596,6 +599,23 @@ static void verity_finish_io(struct dm_verity_io *io, blk_status_t status) if (!static_branch_unlikely(&use_bh_wq_enabled) || !io->in_bh) verity_fec_finish_io(io); + if (unlikely(status != BLK_STS_OK) && + unlikely(!(bio->bi_opf & REQ_RAHEAD)) && + !verity_is_system_shutting_down()) { + if (v->mode == DM_VERITY_MODE_RESTART || + v->mode == DM_VERITY_MODE_PANIC) + DMERR_LIMIT("%s has error: %s", v->data_dev->name, + blk_status_to_str(status)); + + if (v->mode == DM_VERITY_MODE_RESTART) { + pr_emerg("dm-verity device corrupted\n"); + emergency_restart(); + } + + if (v->mode == DM_VERITY_MODE_PANIC) + panic("dm-verity device corrupted"); + } + bio_endio(bio); } @@ -930,6 +950,41 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) limits->dma_alignment = limits->logical_block_size - 1; } +#ifdef CONFIG_SECURITY + +static int verity_init_sig(struct dm_verity *v, const void *sig, + size_t sig_size) +{ + v->sig_size = sig_size; + + if (sig) { + v->root_digest_sig = kmemdup(sig, v->sig_size, GFP_KERNEL); + if (!v->root_digest_sig) + return -ENOMEM; + } + + return 0; +} + +static void verity_free_sig(struct dm_verity *v) +{ + kfree(v->root_digest_sig); +} + +#else + +static inline int verity_init_sig(struct dm_verity *v, const void *sig, + size_t sig_size) +{ + return 0; +} + +static inline void verity_free_sig(struct dm_verity *v) +{ +} + +#endif /* CONFIG_SECURITY */ + static void verity_dtr(struct dm_target *ti) { struct dm_verity *v = ti->private; @@ -949,6 +1004,7 @@ static void verity_dtr(struct dm_target *ti) kfree(v->initial_hashstate); kfree(v->root_digest); kfree(v->zero_digest); + verity_free_sig(v); if (v->ahash_tfm) { static_branch_dec(&ahash_enabled); @@ -1418,6 +1474,13 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->error = "Root hash verification failed"; goto bad; } + + r = verity_init_sig(v, verify_args.sig, verify_args.sig_size); + if (r < 0) { + ti->error = "Cannot allocate root digest signature"; + goto bad; + } + v->hash_per_block_bits = __fls((1 << v->hash_dev_block_bits) / v->digest_size); @@ -1559,8 +1622,79 @@ int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest, unsigned i return 0; } +#ifdef CONFIG_SECURITY + +#ifdef CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG + +static int verity_security_set_signature(struct block_device *bdev, + struct dm_verity *v) +{ + /* + * if the dm-verity target is unsigned, v->root_digest_sig will + * be NULL, and the hook call is still required to let LSMs mark + * the device as unsigned. This information is crucial for LSMs to + * block operations such as execution on unsigned files + */ + return security_bdev_setintegrity(bdev, + LSM_INT_DMVERITY_SIG_VALID, + v->root_digest_sig, + v->sig_size); +} + +#else + +static inline int verity_security_set_signature(struct block_device *bdev, + struct dm_verity *v) +{ + return 0; +} + +#endif /* CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG */ + +/* + * Expose verity target's root hash and signature data to LSMs before resume. + * + * Returns 0 on success, or -ENOMEM if the system is out of memory. + */ +static int verity_preresume(struct dm_target *ti) +{ + struct block_device *bdev; + struct dm_verity_digest root_digest; + struct dm_verity *v; + int r; + + v = ti->private; + bdev = dm_disk(dm_table_get_md(ti->table))->part0; + root_digest.digest = v->root_digest; + root_digest.digest_len = v->digest_size; + if (static_branch_unlikely(&ahash_enabled) && !v->shash_tfm) + root_digest.alg = crypto_ahash_alg_name(v->ahash_tfm); + else + root_digest.alg = crypto_shash_alg_name(v->shash_tfm); + + r = security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, &root_digest, + sizeof(root_digest)); + if (r) + return r; + + r = verity_security_set_signature(bdev, v); + if (r) + goto bad; + + return 0; + +bad: + + security_bdev_setintegrity(bdev, LSM_INT_DMVERITY_ROOTHASH, NULL, 0); + + return r; +} + +#endif /* CONFIG_SECURITY */ + static struct target_type verity_target = { .name = "verity", +/* Note: the LSMs depend on the singleton and immutable features */ .features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE, .version = {1, 10, 0}, .module = THIS_MODULE, @@ -1571,6 +1705,9 @@ static struct target_type verity_target = { .prepare_ioctl = verity_prepare_ioctl, .iterate_devices = verity_iterate_devices, .io_hints = verity_io_hints, +#ifdef CONFIG_SECURITY + .preresume = verity_preresume, +#endif /* CONFIG_SECURITY */ }; module_dm(verity); |