|
@@ -14,12 +14,10 @@
|
|
|
* access behavior.
|
|
|
*/
|
|
|
|
|
|
-#include "dm-bufio.h"
|
|
|
+#include "dm-verity.h"
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
-#include <linux/device-mapper.h>
|
|
|
#include <linux/reboot.h>
|
|
|
-#include <crypto/hash.h>
|
|
|
|
|
|
#define DM_MSG_PREFIX "verity"
|
|
|
|
|
@@ -28,7 +26,6 @@
|
|
|
|
|
|
#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
|
|
|
|
|
|
-#define DM_VERITY_MAX_LEVELS 63
|
|
|
#define DM_VERITY_MAX_CORRUPTED_ERRS 100
|
|
|
|
|
|
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
|
|
@@ -40,72 +37,6 @@ static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
|
|
|
|
|
|
module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
|
|
|
|
|
|
-enum verity_mode {
|
|
|
- DM_VERITY_MODE_EIO,
|
|
|
- DM_VERITY_MODE_LOGGING,
|
|
|
- DM_VERITY_MODE_RESTART
|
|
|
-};
|
|
|
-
|
|
|
-enum verity_block_type {
|
|
|
- DM_VERITY_BLOCK_TYPE_DATA,
|
|
|
- DM_VERITY_BLOCK_TYPE_METADATA
|
|
|
-};
|
|
|
-
|
|
|
-struct dm_verity {
|
|
|
- struct dm_dev *data_dev;
|
|
|
- struct dm_dev *hash_dev;
|
|
|
- struct dm_target *ti;
|
|
|
- struct dm_bufio_client *bufio;
|
|
|
- char *alg_name;
|
|
|
- struct crypto_shash *tfm;
|
|
|
- u8 *root_digest; /* digest of the root block */
|
|
|
- u8 *salt; /* salt: its size is salt_size */
|
|
|
- unsigned salt_size;
|
|
|
- sector_t data_start; /* data offset in 512-byte sectors */
|
|
|
- sector_t hash_start; /* hash start in blocks */
|
|
|
- sector_t data_blocks; /* the number of data blocks */
|
|
|
- sector_t hash_blocks; /* the number of hash blocks */
|
|
|
- unsigned char data_dev_block_bits; /* log2(data blocksize) */
|
|
|
- unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
|
|
|
- unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
|
|
|
- unsigned char levels; /* the number of tree levels */
|
|
|
- unsigned char version;
|
|
|
- unsigned digest_size; /* digest size for the current hash algorithm */
|
|
|
- unsigned shash_descsize;/* the size of temporary space for crypto */
|
|
|
- int hash_failed; /* set to 1 if hash of any block failed */
|
|
|
- enum verity_mode mode; /* mode for handling verification errors */
|
|
|
- unsigned corrupted_errs;/* Number of errors for corrupted blocks */
|
|
|
-
|
|
|
- struct workqueue_struct *verify_wq;
|
|
|
-
|
|
|
- /* starting blocks for each tree level. 0 is the lowest level. */
|
|
|
- sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
|
|
|
-};
|
|
|
-
|
|
|
-struct dm_verity_io {
|
|
|
- struct dm_verity *v;
|
|
|
-
|
|
|
- /* original value of bio->bi_end_io */
|
|
|
- bio_end_io_t *orig_bi_end_io;
|
|
|
-
|
|
|
- sector_t block;
|
|
|
- unsigned n_blocks;
|
|
|
-
|
|
|
- struct bvec_iter iter;
|
|
|
-
|
|
|
- struct work_struct work;
|
|
|
-
|
|
|
- /*
|
|
|
- * Three variably-size fields follow this struct:
|
|
|
- *
|
|
|
- * u8 hash_desc[v->shash_descsize];
|
|
|
- * u8 real_digest[v->digest_size];
|
|
|
- * u8 want_digest[v->digest_size];
|
|
|
- *
|
|
|
- * To access them use: io_hash_desc(), io_real_digest() and io_want_digest().
|
|
|
- */
|
|
|
-};
|
|
|
-
|
|
|
struct dm_verity_prefetch_work {
|
|
|
struct work_struct work;
|
|
|
struct dm_verity *v;
|
|
@@ -113,21 +44,6 @@ struct dm_verity_prefetch_work {
|
|
|
unsigned n_blocks;
|
|
|
};
|
|
|
|
|
|
-static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)
|
|
|
-{
|
|
|
- return (struct shash_desc *)(io + 1);
|
|
|
-}
|
|
|
-
|
|
|
-static u8 *io_real_digest(struct dm_verity *v, struct dm_verity_io *io)
|
|
|
-{
|
|
|
- return (u8 *)(io + 1) + v->shash_descsize;
|
|
|
-}
|
|
|
-
|
|
|
-static u8 *io_want_digest(struct dm_verity *v, struct dm_verity_io *io)
|
|
|
-{
|
|
|
- return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Auxiliary structure appended to each dm-bufio buffer. If the value
|
|
|
* hash_verified is nonzero, hash of the block has been verified.
|
|
@@ -236,8 +152,8 @@ static int verity_hash_final(struct dm_verity *v, struct shash_desc *desc,
|
|
|
return r;
|
|
|
}
|
|
|
|
|
|
-static int verity_hash(struct dm_verity *v, struct shash_desc *desc,
|
|
|
- const u8 *data, size_t len, u8 *digest)
|
|
|
+int verity_hash(struct dm_verity *v, struct shash_desc *desc,
|
|
|
+ const u8 *data, size_t len, u8 *digest)
|
|
|
{
|
|
|
int r;
|
|
|
|
|
@@ -325,12 +241,12 @@ out:
|
|
|
* Verify hash of a metadata block pertaining to the specified data block
|
|
|
* ("block" argument) at a specified level ("level" argument).
|
|
|
*
|
|
|
- * On successful return, io_want_digest(v, io) contains the hash value for
|
|
|
- * a lower tree level or for the data block (if we're at the lowest leve).
|
|
|
+ * On successful return, verity_io_want_digest(v, io) contains the hash value
|
|
|
+ * for a lower tree level or for the data block (if we're at the lowest level).
|
|
|
*
|
|
|
* If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
|
|
|
* If "skip_unverified" is false, unverified buffer is hashed and verified
|
|
|
- * against current value of io_want_digest(v, io).
|
|
|
+ * against current value of verity_io_want_digest(v, io).
|
|
|
*/
|
|
|
static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
|
|
|
sector_t block, int level, bool skip_unverified,
|
|
@@ -357,13 +273,13 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
|
|
|
goto release_ret_r;
|
|
|
}
|
|
|
|
|
|
- r = verity_hash(v, io_hash_desc(v, io),
|
|
|
+ r = verity_hash(v, verity_io_hash_desc(v, io),
|
|
|
data, 1 << v->hash_dev_block_bits,
|
|
|
- io_real_digest(v, io));
|
|
|
+ verity_io_real_digest(v, io));
|
|
|
if (unlikely(r < 0))
|
|
|
goto release_ret_r;
|
|
|
|
|
|
- if (likely(memcmp(io_real_digest(v, io), want_digest,
|
|
|
+ if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
|
|
|
v->digest_size) == 0))
|
|
|
aux->hash_verified = 1;
|
|
|
else if (verity_handle_err(v,
|
|
@@ -387,8 +303,8 @@ release_ret_r:
|
|
|
* Find a hash for a given block, write it to digest and verify the integrity
|
|
|
* of the hash tree if necessary.
|
|
|
*/
|
|
|
-static int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
|
|
|
- sector_t block, u8 *digest)
|
|
|
+int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
|
|
|
+ sector_t block, u8 *digest)
|
|
|
{
|
|
|
int i;
|
|
|
int r;
|
|
@@ -430,10 +346,10 @@ static int verity_verify_io(struct dm_verity_io *io)
|
|
|
for (b = 0; b < io->n_blocks; b++) {
|
|
|
int r;
|
|
|
unsigned todo;
|
|
|
- struct shash_desc *desc = io_hash_desc(v, io);
|
|
|
+ struct shash_desc *desc = verity_io_hash_desc(v, io);
|
|
|
|
|
|
r = verity_hash_for_block(v, io, io->block + b,
|
|
|
- io_want_digest(v, io));
|
|
|
+ verity_io_want_digest(v, io));
|
|
|
if (unlikely(r < 0))
|
|
|
return r;
|
|
|
|
|
@@ -462,12 +378,12 @@ static int verity_verify_io(struct dm_verity_io *io)
|
|
|
todo -= len;
|
|
|
} while (todo);
|
|
|
|
|
|
- r = verity_hash_final(v, desc, io_real_digest(v, io));
|
|
|
+ r = verity_hash_final(v, desc, verity_io_real_digest(v, io));
|
|
|
if (unlikely(r < 0))
|
|
|
return r;
|
|
|
|
|
|
- if (likely(memcmp(io_real_digest(v, io),
|
|
|
- io_want_digest(v, io), v->digest_size) == 0))
|
|
|
+ if (likely(memcmp(verity_io_real_digest(v, io),
|
|
|
+ verity_io_want_digest(v, io), v->digest_size) == 0))
|
|
|
continue;
|
|
|
else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
|
|
|
io->block + b))
|