Merge "init: do_mounts: Add a dummy definition for dm_table_put"

This commit is contained in:
Linux Build Service Account 2016-10-31 13:04:16 -07:00 committed by Gerrit - the friendly Code Review server
commit 44057c87c7
10 changed files with 1119 additions and 24 deletions

View file

@ -517,4 +517,20 @@ config DM_LOG_WRITES
If unsure, say N.
config DM_ANDROID_VERITY
bool "Android verity target support"
depends on DM_VERITY
depends on X509_CERTIFICATE_PARSER
depends on SYSTEM_TRUSTED_KEYRING
depends on PUBLIC_KEY_ALGO_RSA
depends on KEYS
depends on ASYMMETRIC_KEY_TYPE
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
---help---
This device-mapper target is virtually a VERITY target. This
target is setup by reading the metadata contents piggybacked
to the actual data blocks in the block device. The signature
of the metadata contents are verified against the key included
in the system keyring. Upon success, the underlying verity
target is setup.
endif # MD

View file

@ -69,3 +69,7 @@ endif
ifeq ($(CONFIG_DM_VERITY_FEC),y)
dm-verity-objs += dm-verity-fec.o
endif
ifeq ($(CONFIG_DM_ANDROID_VERITY),y)
dm-verity-objs += dm-android-verity.o
endif

View file

@ -0,0 +1,925 @@
/*
* Copyright (C) 2015 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/buffer_head.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/device-mapper.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/key.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/of.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/setup.h>
#include <crypto/hash.h>
#include <crypto/public_key.h>
#include <crypto/sha.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "dm-verity.h"
#include "dm-android-verity.h"
static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH];
static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH];
static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH];
static char buildvariant[BUILD_VARIANT];
static bool target_added;
static bool verity_enabled = true;
struct dentry *debug_dir;
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
static struct target_type android_verity_target = {
.name = "android-verity",
.version = {1, 0, 0},
.module = THIS_MODULE,
.ctr = android_verity_ctr,
.dtr = verity_dtr,
.map = verity_map,
.status = verity_status,
.prepare_ioctl = verity_prepare_ioctl,
.iterate_devices = verity_iterate_devices,
.io_hints = verity_io_hints,
};
static int __init verified_boot_state_param(char *line)
{
strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate));
return 1;
}
__setup("androidboot.verifiedbootstate=", verified_boot_state_param);
static int __init verity_mode_param(char *line)
{
strlcpy(veritymode, line, sizeof(veritymode));
return 1;
}
__setup("androidboot.veritymode=", verity_mode_param);
static int __init verity_keyid_param(char *line)
{
strlcpy(veritykeyid, line, sizeof(veritykeyid));
return 1;
}
__setup("veritykeyid=", verity_keyid_param);
static int __init verity_buildvariant(char *line)
{
strlcpy(buildvariant, line, sizeof(buildvariant));
return 1;
}
__setup("buildvariant=", verity_buildvariant);
static inline bool default_verity_key_id(void)
{
return veritykeyid[0] != '\0';
}
static inline bool is_eng(void)
{
static const char typeeng[] = "eng";
return !strncmp(buildvariant, typeeng, sizeof(typeeng));
}
static inline bool is_userdebug(void)
{
static const char typeuserdebug[] = "userdebug";
return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug));
}
static int table_extract_mpi_array(struct public_key_signature *pks,
const void *data, size_t len)
{
MPI mpi = mpi_read_raw_data(data, len);
if (!mpi) {
DMERR("Error while allocating mpi array");
return -ENOMEM;
}
pks->mpi[0] = mpi;
pks->nr_mpi = 1;
return 0;
}
static struct public_key_signature *table_make_digest(
enum hash_algo hash,
const void *table,
unsigned long table_len)
{
struct public_key_signature *pks = NULL;
struct crypto_shash *tfm;
struct shash_desc *desc;
size_t digest_size, desc_size;
int ret;
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be.
*/
tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
if (IS_ERR(tfm))
return ERR_CAST(tfm);
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
digest_size = crypto_shash_digestsize(tfm);
/* We allocate the hash operational data storage on the end of out
* context data and the digest output buffer on the end of that.
*/
ret = -ENOMEM;
pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
if (!pks)
goto error;
pks->pkey_hash_algo = hash;
pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
pks->digest_size = digest_size;
desc = (struct shash_desc *)(pks + 1);
desc->tfm = tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
ret = crypto_shash_init(desc);
if (ret < 0)
goto error;
ret = crypto_shash_finup(desc, table, table_len, pks->digest);
if (ret < 0)
goto error;
crypto_free_shash(tfm);
return pks;
error:
kfree(pks);
crypto_free_shash(tfm);
return ERR_PTR(ret);
}
static int read_block_dev(struct bio_read *payload, struct block_device *bdev,
sector_t offset, int length)
{
struct bio *bio;
int err = 0, i;
payload->number_of_pages = DIV_ROUND_UP(length, PAGE_SIZE);
bio = bio_alloc(GFP_KERNEL, payload->number_of_pages);
if (!bio) {
DMERR("Error while allocating bio");
return -ENOMEM;
}
bio->bi_bdev = bdev;
bio->bi_iter.bi_sector = offset;
payload->page_io = kzalloc(sizeof(struct page *) *
payload->number_of_pages, GFP_KERNEL);
if (!payload->page_io) {
DMERR("page_io array alloc failed");
err = -ENOMEM;
goto free_bio;
}
for (i = 0; i < payload->number_of_pages; i++) {
payload->page_io[i] = alloc_page(GFP_KERNEL);
if (!payload->page_io[i]) {
DMERR("alloc_page failed");
err = -ENOMEM;
goto free_pages;
}
if (!bio_add_page(bio, payload->page_io[i], PAGE_SIZE, 0)) {
DMERR("bio_add_page error");
err = -EIO;
goto free_pages;
}
}
if (!submit_bio_wait(READ, bio))
/* success */
goto free_bio;
DMERR("bio read failed");
err = -EIO;
free_pages:
for (i = 0; i < payload->number_of_pages; i++)
if (payload->page_io[i])
__free_page(payload->page_io[i]);
kfree(payload->page_io);
free_bio:
bio_put(bio);
return err;
}
static inline u64 fec_div_round_up(u64 x, u64 y)
{
u64 remainder;
return div64_u64_rem(x, y, &remainder) +
(remainder > 0 ? 1 : 0);
}
static inline void populate_fec_metadata(struct fec_header *header,
struct fec_ecc_metadata *ecc)
{
ecc->blocks = fec_div_round_up(le64_to_cpu(header->inp_size),
FEC_BLOCK_SIZE);
ecc->roots = le32_to_cpu(header->roots);
ecc->start = le64_to_cpu(header->inp_size);
}
static inline int validate_fec_header(struct fec_header *header, u64 offset)
{
/* move offset to make the sanity check work for backup header
* as well. */
offset -= offset % FEC_BLOCK_SIZE;
if (le32_to_cpu(header->magic) != FEC_MAGIC ||
le32_to_cpu(header->version) != FEC_VERSION ||
le32_to_cpu(header->size) != sizeof(struct fec_header) ||
le32_to_cpu(header->roots) == 0 ||
le32_to_cpu(header->roots) >= FEC_RSM)
return -EINVAL;
return 0;
}
static int extract_fec_header(dev_t dev, struct fec_header *fec,
struct fec_ecc_metadata *ecc)
{
u64 device_size;
struct bio_read payload;
int i, err = 0;
struct block_device *bdev;
bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
if (IS_ERR_OR_NULL(bdev)) {
DMERR("bdev get error");
return PTR_ERR(bdev);
}
device_size = i_size_read(bdev->bd_inode);
/* fec metadata size is a power of 2 and PAGE_SIZE
* is a power of 2 as well.
*/
BUG_ON(FEC_BLOCK_SIZE > PAGE_SIZE);
/* 512 byte sector alignment */
BUG_ON(((device_size - FEC_BLOCK_SIZE) % (1 << SECTOR_SHIFT)) != 0);
err = read_block_dev(&payload, bdev, (device_size -
FEC_BLOCK_SIZE) / (1 << SECTOR_SHIFT), FEC_BLOCK_SIZE);
if (err) {
DMERR("Error while reading verity metadata");
goto error;
}
BUG_ON(sizeof(struct fec_header) > PAGE_SIZE);
memcpy(fec, page_address(payload.page_io[0]),
sizeof(*fec));
ecc->valid = true;
if (validate_fec_header(fec, device_size - FEC_BLOCK_SIZE)) {
/* Try the backup header */
memcpy(fec, page_address(payload.page_io[0]) + FEC_BLOCK_SIZE
- sizeof(*fec) ,
sizeof(*fec));
if (validate_fec_header(fec, device_size -
sizeof(struct fec_header)))
ecc->valid = false;
}
if (ecc->valid)
populate_fec_metadata(fec, ecc);
for (i = 0; i < payload.number_of_pages; i++)
__free_page(payload.page_io[i]);
kfree(payload.page_io);
error:
blkdev_put(bdev, FMODE_READ);
return err;
}
static void find_metadata_offset(struct fec_header *fec,
struct block_device *bdev, u64 *metadata_offset)
{
u64 device_size;
device_size = i_size_read(bdev->bd_inode);
if (le32_to_cpu(fec->magic) == FEC_MAGIC)
*metadata_offset = le64_to_cpu(fec->inp_size) -
VERITY_METADATA_SIZE;
else
*metadata_offset = device_size - VERITY_METADATA_SIZE;
}
static int find_size(dev_t dev, u64 *device_size)
{
struct block_device *bdev;
bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
if (IS_ERR_OR_NULL(bdev)) {
DMERR("blkdev_get_by_dev failed");
return PTR_ERR(bdev);
}
*device_size = i_size_read(bdev->bd_inode);
*device_size >>= SECTOR_SHIFT;
DMINFO("blkdev size in sectors: %llu", *device_size);
blkdev_put(bdev, FMODE_READ);
return 0;
}
static int verify_header(struct android_metadata_header *header)
{
int retval = -EINVAL;
if (is_userdebug() && le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_DISABLE)
return VERITY_STATE_DISABLE;
if (!(le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_NUMBER) ||
(le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_DISABLE)) {
DMERR("Incorrect magic number");
return retval;
}
if (le32_to_cpu(header->protocol_version) !=
VERITY_METADATA_VERSION) {
DMERR("Unsupported version %u",
le32_to_cpu(header->protocol_version));
return retval;
}
return 0;
}
static int extract_metadata(dev_t dev, struct fec_header *fec,
struct android_metadata **metadata,
bool *verity_enabled)
{
struct block_device *bdev;
struct android_metadata_header *header;
int i;
u32 table_length, copy_length, offset;
u64 metadata_offset;
struct bio_read payload;
int err = 0;
bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
if (IS_ERR_OR_NULL(bdev)) {
DMERR("blkdev_get_by_dev failed");
return -ENODEV;
}
find_metadata_offset(fec, bdev, &metadata_offset);
/* Verity metadata size is a power of 2 and PAGE_SIZE
* is a power of 2 as well.
* PAGE_SIZE is also a multiple of 512 bytes.
*/
if (VERITY_METADATA_SIZE > PAGE_SIZE)
BUG_ON(VERITY_METADATA_SIZE % PAGE_SIZE != 0);
/* 512 byte sector alignment */
BUG_ON(metadata_offset % (1 << SECTOR_SHIFT) != 0);
err = read_block_dev(&payload, bdev, metadata_offset /
(1 << SECTOR_SHIFT), VERITY_METADATA_SIZE);
if (err) {
DMERR("Error while reading verity metadata");
goto blkdev_release;
}
header = kzalloc(sizeof(*header), GFP_KERNEL);
if (!header) {
DMERR("kzalloc failed for header");
err = -ENOMEM;
goto free_payload;
}
memcpy(header, page_address(payload.page_io[0]),
sizeof(*header));
DMINFO("bio magic_number:%u protocol_version:%d table_length:%u",
le32_to_cpu(header->magic_number),
le32_to_cpu(header->protocol_version),
le32_to_cpu(header->table_length));
err = verify_header(header);
if (err == VERITY_STATE_DISABLE) {
DMERR("Mounting root with verity disabled");
*verity_enabled = false;
/* we would still have to read the metadata to figure out
* the data blocks size. Or may be could map the entire
* partition similar to mounting the device.
*
* Reset error as well as the verity_enabled flag is changed.
*/
err = 0;
} else if (err)
goto free_header;
*metadata = kzalloc(sizeof(**metadata), GFP_KERNEL);
if (!*metadata) {
DMERR("kzalloc for metadata failed");
err = -ENOMEM;
goto free_header;
}
(*metadata)->header = header;
table_length = le32_to_cpu(header->table_length);
if (table_length == 0 ||
table_length > (VERITY_METADATA_SIZE -
sizeof(struct android_metadata_header))) {
DMERR("table_length too long");
err = -EINVAL;
goto free_metadata;
}
(*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL);
if (!(*metadata)->verity_table) {
DMERR("kzalloc verity_table failed");
err = -ENOMEM;
goto free_metadata;
}
if (sizeof(struct android_metadata_header) +
table_length <= PAGE_SIZE) {
memcpy((*metadata)->verity_table,
page_address(payload.page_io[0])
+ sizeof(struct android_metadata_header),
table_length);
} else {
copy_length = PAGE_SIZE -
sizeof(struct android_metadata_header);
memcpy((*metadata)->verity_table,
page_address(payload.page_io[0])
+ sizeof(struct android_metadata_header),
copy_length);
table_length -= copy_length;
offset = copy_length;
i = 1;
while (table_length != 0) {
if (table_length > PAGE_SIZE) {
memcpy((*metadata)->verity_table + offset,
page_address(payload.page_io[i]),
PAGE_SIZE);
offset += PAGE_SIZE;
table_length -= PAGE_SIZE;
} else {
memcpy((*metadata)->verity_table + offset,
page_address(payload.page_io[i]),
table_length);
table_length = 0;
}
i++;
}
}
(*metadata)->verity_table[table_length] = '\0';
DMINFO("verity_table: %s", (*metadata)->verity_table);
goto free_payload;
free_metadata:
kfree(*metadata);
free_header:
kfree(header);
free_payload:
for (i = 0; i < payload.number_of_pages; i++)
if (payload.page_io[i])
__free_page(payload.page_io[i]);
kfree(payload.page_io);
blkdev_release:
blkdev_put(bdev, FMODE_READ);
return err;
}
/* helper functions to extract properties from dts */
const char *find_dt_value(const char *name)
{
struct device_node *firmware;
const char *value;
firmware = of_find_node_by_path("/firmware/android");
if (!firmware)
return NULL;
value = of_get_property(firmware, name, NULL);
of_node_put(firmware);
return value;
}
static int verity_mode(void)
{
static const char enforcing[] = "enforcing";
static const char verified_mode_prop[] = "veritymode";
const char *value;
value = find_dt_value(verified_mode_prop);
if (!value)
value = veritymode;
if (!strncmp(value, enforcing, sizeof(enforcing) - 1))
return DM_VERITY_MODE_RESTART;
return DM_VERITY_MODE_EIO;
}
static int verify_verity_signature(char *key_id,
struct android_metadata *metadata)
{
key_ref_t key_ref;
struct key *key;
struct public_key_signature *pks = NULL;
int retval = -EINVAL;
key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1),
&key_type_asymmetric, key_id);
if (IS_ERR(key_ref)) {
DMERR("keyring: key not found");
return -ENOKEY;
}
key = key_ref_to_ptr(key_ref);
pks = table_make_digest(HASH_ALGO_SHA256,
(const void *)metadata->verity_table,
le32_to_cpu(metadata->header->table_length));
if (IS_ERR(pks)) {
DMERR("hashing failed");
goto error;
}
retval = table_extract_mpi_array(pks, &metadata->header->signature[0],
RSANUMBYTES);
if (retval < 0) {
DMERR("Error extracting mpi %d", retval);
goto error;
}
retval = verify_signature(key, pks);
mpi_free(pks->rsa.s);
error:
kfree(pks);
key_put(key);
return retval;
}
static void handle_error(void)
{
int mode = verity_mode();
if (mode == DM_VERITY_MODE_RESTART) {
DMERR("triggering restart");
kernel_restart("dm-verity device corrupted");
} else {
DMERR("Mounting verity root failed");
}
}
static inline bool test_mult_overflow(sector_t a, u32 b)
{
sector_t r = (sector_t)~0ULL;
sector_div(r, b);
return a > r;
}
static int add_as_linear_device(struct dm_target *ti, char *dev)
{
/*Move to linear mapping defines*/
char *linear_table_args[DM_LINEAR_ARGS] = {dev,
DM_LINEAR_TARGET_OFFSET};
int err = 0;
android_verity_target.dtr = dm_linear_dtr,
android_verity_target.map = dm_linear_map,
android_verity_target.status = dm_linear_status,
android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl,
android_verity_target.iterate_devices = dm_linear_iterate_devices,
android_verity_target.io_hints = NULL;
err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args);
if (!err) {
DMINFO("Added android-verity as a linear target");
target_added = true;
} else
DMERR("Failed to add android-verity as linear target");
return err;
}
/*
* Target parameters:
* <key id> Key id of the public key in the system keyring.
* Verity metadata's signature would be verified against
* this. If the key id contains spaces, replace them
* with '#'.
* <block device> The block device for which dm-verity is being setup.
*/
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
dev_t uninitialized_var(dev);
struct android_metadata *metadata = NULL;
int err = 0, i, mode;
char *key_id, *table_ptr, dummy, *target_device,
*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
/* One for specifying number of opt args and one for mode */
sector_t data_sectors;
u32 data_block_size;
unsigned int no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS;
struct fec_header uninitialized_var(fec);
struct fec_ecc_metadata uninitialized_var(ecc);
char buf[FEC_ARG_LENGTH], *buf_ptr;
unsigned long long tmpll;
u64 uninitialized_var(device_size);
if (argc == 1) {
/* Use the default keyid */
if (default_verity_key_id())
key_id = veritykeyid;
else if (!is_eng()) {
DMERR("veritykeyid= is not set");
handle_error();
return -EINVAL;
}
} else if (argc == 2)
key_id = argv[1];
else {
DMERR("Incorrect number of arguments");
handle_error();
return -EINVAL;
}
target_device = argv[0];
dev = name_to_dev_t(target_device);
if (!dev) {
DMERR("no dev found for %s", target_device);
handle_error();
return -EINVAL;
}
if (is_eng()) {
err = find_size(dev, &device_size);
if (err) {
DMERR("error finding bdev size");
handle_error();
return err;
}
ti->len = device_size;
err = add_as_linear_device(ti, target_device);
if (err) {
handle_error();
return err;
}
verity_enabled = false;
return 0;
}
strreplace(key_id, '#', ' ');
DMINFO("key:%s dev:%s", key_id, target_device);
if (extract_fec_header(dev, &fec, &ecc)) {
DMERR("Error while extracting fec header");
handle_error();
return -EINVAL;
}
err = extract_metadata(dev, &fec, &metadata, &verity_enabled);
if (err) {
DMERR("Error while extracting metadata");
handle_error();
goto free_metadata;
}
if (verity_enabled) {
err = verify_verity_signature(key_id, metadata);
if (err) {
DMERR("Signature verification failed");
handle_error();
goto free_metadata;
} else
DMINFO("Signature verification success");
}
table_ptr = metadata->verity_table;
for (i = 0; i < VERITY_TABLE_ARGS; i++) {
verity_table_args[i] = strsep(&table_ptr, " ");
if (verity_table_args[i] == NULL)
break;
}
if (i != VERITY_TABLE_ARGS) {
DMERR("Verity table not in the expected format");
err = -EINVAL;
handle_error();
goto free_metadata;
}
if (sscanf(verity_table_args[5], "%llu%c", &tmpll, &dummy)
!= 1) {
DMERR("Verity table not in the expected format");
handle_error();
err = -EINVAL;
goto free_metadata;
}
if (tmpll > ULONG_MAX) {
DMERR("<num_data_blocks> too large. Forgot to turn on CONFIG_LBDAF?");
handle_error();
err = -EINVAL;
goto free_metadata;
}
data_sectors = tmpll;
if (sscanf(verity_table_args[3], "%u%c", &data_block_size, &dummy)
!= 1) {
DMERR("Verity table not in the expected format");
handle_error();
err = -EINVAL;
goto free_metadata;
}
if (test_mult_overflow(data_sectors, data_block_size >>
SECTOR_SHIFT)) {
DMERR("data_sectors too large");
handle_error();
err = -EOVERFLOW;
goto free_metadata;
}
data_sectors *= data_block_size >> SECTOR_SHIFT;
DMINFO("Data sectors %llu", (unsigned long long)data_sectors);
/* update target length */
ti->len = data_sectors;
/* Setup linear target and free */
if (!verity_enabled) {
err = add_as_linear_device(ti, target_device);
goto free_metadata;
}
/*substitute data_dev and hash_dev*/
verity_table_args[1] = target_device;
verity_table_args[2] = target_device;
mode = verity_mode();
if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) {
if (mode) {
err = snprintf(buf, FEC_ARG_LENGTH,
"%u %s " VERITY_TABLE_OPT_FEC_FORMAT,
1 + VERITY_TABLE_OPT_FEC_ARGS,
mode == DM_VERITY_MODE_RESTART ?
VERITY_TABLE_OPT_RESTART :
VERITY_TABLE_OPT_LOGGING,
target_device,
ecc.start / FEC_BLOCK_SIZE, ecc.blocks,
ecc.roots);
} else {
err = snprintf(buf, FEC_ARG_LENGTH,
"%u " VERITY_TABLE_OPT_FEC_FORMAT,
VERITY_TABLE_OPT_FEC_ARGS, target_device,
ecc.start / FEC_BLOCK_SIZE, ecc.blocks,
ecc.roots);
}
} else if (mode) {
err = snprintf(buf, FEC_ARG_LENGTH,
"2 " VERITY_TABLE_OPT_IGNZERO " %s",
mode == DM_VERITY_MODE_RESTART ?
VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING);
} else {
err = snprintf(buf, FEC_ARG_LENGTH, "1 %s",
"ignore_zero_blocks");
}
if (err < 0 || err >= FEC_ARG_LENGTH)
goto free_metadata;
buf_ptr = buf;
for (i = VERITY_TABLE_ARGS; i < (VERITY_TABLE_ARGS +
VERITY_TABLE_OPT_FEC_ARGS + 2); i++) {
verity_table_args[i] = strsep(&buf_ptr, " ");
if (verity_table_args[i] == NULL) {
no_of_args = i;
break;
}
}
err = verity_ctr(ti, no_of_args, verity_table_args);
if (err)
DMERR("android-verity failed to mount as verity target");
else {
target_added = true;
DMINFO("android-verity mounted as verity target");
}
free_metadata:
if (metadata) {
kfree(metadata->header);
kfree(metadata->verity_table);
}
kfree(metadata);
return err;
}
static int __init dm_android_verity_init(void)
{
int r;
struct dentry *file;
r = dm_register_target(&android_verity_target);
if (r < 0)
DMERR("register failed %d", r);
/* Tracks the status of the last added target */
debug_dir = debugfs_create_dir("android_verity", NULL);
if (IS_ERR_OR_NULL(debug_dir)) {
DMERR("Cannot create android_verity debugfs directory: %ld",
PTR_ERR(debug_dir));
goto end;
}
file = debugfs_create_bool("target_added", S_IRUGO, debug_dir,
&target_added);
if (IS_ERR_OR_NULL(file)) {
DMERR("Cannot create android_verity debugfs directory: %ld",
PTR_ERR(debug_dir));
debugfs_remove_recursive(debug_dir);
goto end;
}
file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir,
&verity_enabled);
if (IS_ERR_OR_NULL(file)) {
DMERR("Cannot create android_verity debugfs directory: %ld",
PTR_ERR(debug_dir));
debugfs_remove_recursive(debug_dir);
}
end:
return r;
}
static void __exit dm_android_verity_exit(void)
{
if (!IS_ERR_OR_NULL(debug_dir))
debugfs_remove_recursive(debug_dir);
dm_unregister_target(&android_verity_target);
}
module_init(dm_android_verity_init);
module_exit(dm_android_verity_exit);

View file

@ -0,0 +1,124 @@
/*
* Copyright (C) 2015 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef DM_ANDROID_VERITY_H
#define DM_ANDROID_VERITY_H
#include <crypto/sha.h>
#define RSANUMBYTES 256
#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56
#define VERITY_METADATA_VERSION 0
#define VERITY_STATE_DISABLE 1
#define DATA_BLOCK_SIZE (4 * 1024)
#define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE)
#define VERITY_TABLE_ARGS 10
#define VERITY_COMMANDLINE_PARAM_LENGTH 20
#define BUILD_VARIANT 20
/*
* <subject>:<sha1-id> is the format for the identifier.
* subject can either be the Common Name(CN) + Organization Name(O) or
* just the CN if the it is prefixed with O
* From https://tools.ietf.org/html/rfc5280#appendix-A
* ub-organization-name-length INTEGER ::= 64
* ub-common-name-length INTEGER ::= 64
*
* http://lxr.free-electrons.com/source/crypto/asymmetric_keys/x509_cert_parser.c?v=3.9#L278
* ctx->o_size + 2 + ctx->cn_size + 1
* + 41 characters for ":" and sha1 id
* 64 + 2 + 64 + 1 + 1 + 40 (172)
* setting VERITY_DEFAULT_KEY_ID_LENGTH to 200 characters.
*/
#define VERITY_DEFAULT_KEY_ID_LENGTH 200
#define FEC_MAGIC 0xFECFECFE
#define FEC_BLOCK_SIZE (4 * 1024)
#define FEC_VERSION 0
#define FEC_RSM 255
#define FEC_ARG_LENGTH 300
#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
#define VERITY_TABLE_OPT_FEC_FORMAT \
"use_fec_from_device %s fec_start %llu fec_blocks %llu fec_roots %u ignore_zero_blocks"
#define VERITY_TABLE_OPT_FEC_ARGS 9
#define VERITY_DEBUG 0
#define DM_MSG_PREFIX "android-verity"
#define DM_LINEAR_ARGS 2
#define DM_LINEAR_TARGET_OFFSET "0"
/*
* There can be two formats.
* if fec is present
* <data_blocks> <verity_tree> <verity_metdata_32K><fec_data><fec_data_4K>
* if fec is not present
* <data_blocks> <verity_tree> <verity_metdata_32K>
*/
/* TODO: rearrange structure to reduce memory holes
* depends on userspace change.
*/
struct fec_header {
__le32 magic;
__le32 version;
__le32 size;
__le32 roots;
__le32 fec_size;
__le64 inp_size;
u8 hash[SHA256_DIGEST_SIZE];
};
struct android_metadata_header {
__le32 magic_number;
__le32 protocol_version;
char signature[RSANUMBYTES];
__le32 table_length;
};
struct android_metadata {
struct android_metadata_header *header;
char *verity_table;
};
struct fec_ecc_metadata {
bool valid;
u32 roots;
u64 blocks;
u64 rounds;
u64 start;
};
struct bio_read {
struct page **page_io;
int number_of_pages;
};
extern struct target_type linear_target;
extern void dm_linear_dtr(struct dm_target *ti);
extern int dm_linear_map(struct dm_target *ti, struct bio *bio);
extern void dm_linear_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen);
extern int dm_linear_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode);
extern int dm_linear_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data);
extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv);
#endif /* DM_ANDROID_VERITY_H */

View file

@ -25,7 +25,7 @@ struct linear_c {
/*
* Construct a linear mapping: <dev_path> <offset>
*/
static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct linear_c *lc;
unsigned long long tmp;
@ -67,7 +67,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return ret;
}
static void linear_dtr(struct dm_target *ti)
void dm_linear_dtr(struct dm_target *ti)
{
struct linear_c *lc = (struct linear_c *) ti->private;
@ -92,14 +92,14 @@ static void linear_map_bio(struct dm_target *ti, struct bio *bio)
linear_map_sector(ti, bio->bi_iter.bi_sector);
}
static int linear_map(struct dm_target *ti, struct bio *bio)
int dm_linear_map(struct dm_target *ti, struct bio *bio)
{
linear_map_bio(ti, bio);
return DM_MAPIO_REMAPPED;
}
static void linear_status(struct dm_target *ti, status_type_t type,
void dm_linear_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct linear_c *lc = (struct linear_c *) ti->private;
@ -116,7 +116,7 @@ static void linear_status(struct dm_target *ti, status_type_t type,
}
}
static int linear_prepare_ioctl(struct dm_target *ti,
int dm_linear_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode)
{
struct linear_c *lc = (struct linear_c *) ti->private;
@ -133,7 +133,7 @@ static int linear_prepare_ioctl(struct dm_target *ti,
return 0;
}
static int linear_iterate_devices(struct dm_target *ti,
int dm_linear_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct linear_c *lc = ti->private;
@ -145,12 +145,12 @@ static struct target_type linear_target = {
.name = "linear",
.version = {1, 2, 1},
.module = THIS_MODULE,
.ctr = linear_ctr,
.dtr = linear_dtr,
.map = linear_map,
.status = linear_status,
.prepare_ioctl = linear_prepare_ioctl,
.iterate_devices = linear_iterate_devices,
.ctr = dm_linear_ctr,
.dtr = dm_linear_dtr,
.map = dm_linear_map,
.status = dm_linear_status,
.prepare_ioctl = dm_linear_prepare_ioctl,
.iterate_devices = dm_linear_iterate_devices,
};
int __init dm_linear_init(void)

View file

@ -442,6 +442,13 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
if (!verity_fec_is_enabled(v))
return -EOPNOTSUPP;
if (fio->level >= DM_VERITY_FEC_MAX_RECURSION) {
DMWARN_LIMIT("%s: FEC: recursion too deep", v->data_dev->name);
return -EIO;
}
fio->level++;
if (type == DM_VERITY_BLOCK_TYPE_METADATA)
block += v->data_blocks;
@ -456,9 +463,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
*/
offset = block << v->data_dev_block_bits;
res = offset;
div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
res = div64_u64(offset, v->fec->rounds << v->data_dev_block_bits);
/*
* The base RS block we can feed to the interleaver to find out all
@ -475,7 +480,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
if (r < 0) {
r = fec_decode_rsb(v, io, fio, rsb, offset, true);
if (r < 0)
return r;
goto done;
}
if (dest)
@ -485,6 +490,8 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
r = verity_for_bv_block(v, io, iter, fec_bv_copy);
}
done:
fio->level--;
return r;
}
@ -525,6 +532,7 @@ void verity_fec_init_io(struct dm_verity_io *io)
memset(fio->bufs, 0, sizeof(fio->bufs));
fio->nbufs = 0;
fio->output = NULL;
fio->level = 0;
}
/*
@ -680,7 +688,8 @@ static struct attribute *fec_attrs[] = {
static struct kobj_type fec_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = fec_attrs
.default_attrs = fec_attrs,
.release = dm_kobject_release
};
/*

View file

@ -28,6 +28,9 @@
#define DM_VERITY_FEC_BUF_MAX \
(1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS))
/* maximum recursion level for verity_fec_decode */
#define DM_VERITY_FEC_MAX_RECURSION 4
#define DM_VERITY_OPT_FEC_DEV "use_fec_from_device"
#define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks"
#define DM_VERITY_OPT_FEC_START "fec_start"
@ -61,6 +64,7 @@ struct dm_verity_fec_io {
unsigned nbufs; /* number of buffers allocated */
u8 *output; /* buffer for corrected output */
size_t output_pos;
unsigned level; /* recursion level */
};
#ifdef CONFIG_DM_VERITY_FEC

View file

@ -551,7 +551,7 @@ static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io)
* Bio map function. It allocates dm_verity_io structure and bio vector and
* fills them. Then it issues prefetches and the I/O.
*/
static int verity_map(struct dm_target *ti, struct bio *bio)
int verity_map(struct dm_target *ti, struct bio *bio)
{
struct dm_verity *v = ti->private;
struct dm_verity_io *io;
@ -596,7 +596,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
/*
* Status: V (valid) or C (corruption found)
*/
static void verity_status(struct dm_target *ti, status_type_t type,
void verity_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_verity *v = ti->private;
@ -656,7 +656,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
}
}
static int verity_prepare_ioctl(struct dm_target *ti,
int verity_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode)
{
struct dm_verity *v = ti->private;
@ -669,7 +669,7 @@ static int verity_prepare_ioctl(struct dm_target *ti,
return 0;
}
static int verity_iterate_devices(struct dm_target *ti,
int verity_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct dm_verity *v = ti->private;
@ -677,7 +677,7 @@ static int verity_iterate_devices(struct dm_target *ti,
return fn(ti, v->data_dev, v->data_start, ti->len, data);
}
static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct dm_verity *v = ti->private;
@ -690,7 +690,7 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, limits->logical_block_size);
}
static void verity_dtr(struct dm_target *ti)
void verity_dtr(struct dm_target *ti)
{
struct dm_verity *v = ti->private;
@ -817,7 +817,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
* <digest>
* <salt> Hex string or "-" if no salt.
*/
static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
struct dm_arg_set as;

View file

@ -126,4 +126,14 @@ extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, u8 *digest, bool *is_zero);
extern void verity_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen);
extern int verity_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode);
extern int verity_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data);
extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits);
extern void verity_dtr(struct dm_target *ti);
extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
extern int verity_map(struct dm_target *ti, struct bio *bio);
#endif /* DM_VERITY_H */

View file

@ -13,6 +13,9 @@ void mount_block_root(char *name, int flags);
void mount_root(void);
extern int root_mountflags;
struct dm_table;
static inline void dm_table_put(struct dm_table *t) { }
static inline int create_dev(char *name, dev_t dev)
{
sys_unlink(name);