android_kernel_oneplus_msm8998/drivers/md/dm-android-verity.c
Blagovest Kolenichev b65ef47237 Merge android-4.4@59ff2e1 (v4.4.78) into msm-4.4
* refs/heads/tmp-59ff2e1
  Linux 4.4.78
  kvm: vmx: allow host to access guest MSR_IA32_BNDCFGS
  kvm: vmx: Check value written to IA32_BNDCFGS
  kvm: x86: Guest BNDCFGS requires guest MPX support
  kvm: vmx: Do not disable intercepts for BNDCFGS
  KVM: x86: disable MPX if host did not enable MPX XSAVE features
  tracing: Use SOFTIRQ_OFFSET for softirq dectection for more accurate results
  PM / QoS: return -EINVAL for bogus strings
  PM / wakeirq: Convert to SRCU
  sched/topology: Optimize build_group_mask()
  sched/topology: Fix overlapping sched_group_mask
  crypto: caam - fix signals handling
  crypto: sha1-ssse3 - Disable avx2
  crypto: atmel - only treat EBUSY as transient if backlog
  crypto: talitos - Extend max key length for SHA384/512-HMAC and AEAD
  mm: fix overflow check in expand_upwards()
  tpm: Issue a TPM2_Shutdown for TPM2 devices.
  Add "shutdown" to "struct class".
  tpm: Provide strong locking for device removal
  tpm: Get rid of chip->pdev
  selftests/capabilities: Fix the test_execve test
  mnt: Make propagate_umount less slow for overlapping mount propagation trees
  mnt: In propgate_umount handle visiting mounts in any order
  mnt: In umount propagation reparent in a separate pass
  vt: fix unchecked __put_user() in tioclinux ioctls
  exec: Limit arg stack to at most 75% of _STK_LIM
  s390: reduce ELF_ET_DYN_BASE
  powerpc: move ELF_ET_DYN_BASE to 4GB / 4MB
  arm64: move ELF_ET_DYN_BASE to 4GB / 4MB
  arm: move ELF_ET_DYN_BASE to 4MB
  binfmt_elf: use ELF_ET_DYN_BASE only for PIE
  checkpatch: silence perl 5.26.0 unescaped left brace warnings
  fs/dcache.c: fix spin lockup issue on nlru->lock
  mm/list_lru.c: fix list_lru_count_node() to be race free
  kernel/extable.c: mark core_kernel_text notrace
  tools/lib/lockdep: Reduce MAX_LOCK_DEPTH to avoid overflowing lock_chain/: Depth
  parisc/mm: Ensure IRQs are off in switch_mm()
  parisc: DMA API: return error instead of BUG_ON for dma ops on non dma devs
  parisc: use compat_sys_keyctl()
  parisc: Report SIGSEGV instead of SIGBUS when running out of stack
  irqchip/gic-v3: Fix out-of-bound access in gic_set_affinity
  cfg80211: Check if PMKID attribute is of expected size
  cfg80211: Validate frequencies nested in NL80211_ATTR_SCAN_FREQUENCIES
  cfg80211: Define nla_policy for NL80211_ATTR_LOCAL_MESH_POWER_MODE
  brcmfmac: fix possible buffer overflow in brcmf_cfg80211_mgmt_tx()
  rds: tcp: use sock_create_lite() to create the accept socket
  vrf: fix bug_on triggered by rx when destroying a vrf
  net: ipv6: Compare lwstate in detecting duplicate nexthops
  ipv6: dad: don't remove dynamic addresses if link is down
  net: handle NAPI_GRO_FREE_STOLEN_HEAD case also in napi_frags_finish()
  bpf: prevent leaking pointer via xadd on unpriviledged
  net: prevent sign extension in dev_get_stats()
  tcp: reset sk_rx_dst in tcp_disconnect()
  net: dp83640: Avoid NULL pointer dereference.
  ipv6: avoid unregistering inet6_dev for loopback
  net/phy: micrel: configure intterupts after autoneg workaround
  net: sched: Fix one possible panic when no destroy callback
  net_sched: fix error recovery at qdisc creation
  ANDROID: android-verity: mark dev as rw for linear target
  ANDROID: sdcardfs: Remove unnecessary lock
  ANDROID: binder: don't check prio permissions on restore.
  Add BINDER_GET_NODE_DEBUG_INFO ioctl
  UPSTREAM: cpufreq: schedutil: Trace frequency only if it has changed
  UPSTREAM: cpufreq: schedutil: Avoid reducing frequency of busy CPUs prematurely
  UPSTREAM: cpufreq: schedutil: Refactor sugov_next_freq_shared()
  UPSTREAM: cpufreq: schedutil: Fix per-CPU structure initialization in sugov_start()
  UPSTREAM: cpufreq: schedutil: Pass sg_policy to get_next_freq()
  UPSTREAM: cpufreq: schedutil: move cached_raw_freq to struct sugov_policy
  UPSTREAM: cpufreq: schedutil: Rectify comment in sugov_irq_work() function
  UPSTREAM: cpufreq: schedutil: irq-work and mutex are only used in slow path
  UPSTREAM: cpufreq: schedutil: enable fast switch earlier
  UPSTREAM: cpufreq: schedutil: Avoid indented labels
  Linux 4.4.77
  saa7134: fix warm Medion 7134 EEPROM read
  x86/mm/pat: Don't report PAT on CPUs that don't support it
  ext4: check return value of kstrtoull correctly in reserved_clusters_store
  staging: comedi: fix clean-up of comedi_class in comedi_init()
  staging: vt6556: vnt_start Fix missing call to vnt_key_init_table.
  tcp: fix tcp_mark_head_lost to check skb len before fragmenting
  md: fix super_offset endianness in super_1_rdev_size_change
  md: fix incorrect use of lexx_to_cpu in does_sb_need_changing
  perf tools: Use readdir() instead of deprecated readdir_r() again
  perf tests: Remove wrong semicolon in while loop in CQM test
  perf trace: Do not process PERF_RECORD_LOST twice
  perf dwarf: Guard !x86_64 definitions under #ifdef else clause
  perf pmu: Fix misleadingly indented assignment (whitespace)
  perf annotate browser: Fix behaviour of Shift-Tab with nothing focussed
  perf tools: Remove duplicate const qualifier
  perf script: Use readdir() instead of deprecated readdir_r()
  perf thread_map: Use readdir() instead of deprecated readdir_r()
  perf tools: Use readdir() instead of deprecated readdir_r()
  perf bench numa: Avoid possible truncation when using snprintf()
  perf tests: Avoid possible truncation with dirent->d_name + snprintf
  perf scripting perl: Fix compile error with some perl5 versions
  perf thread_map: Correctly size buffer used with dirent->dt_name
  perf intel-pt: Use __fallthrough
  perf top: Use __fallthrough
  tools strfilter: Use __fallthrough
  tools string: Use __fallthrough in perf_atoll()
  tools include: Add a __fallthrough statement
  mqueue: fix a use-after-free in sys_mq_notify()
  RDMA/uverbs: Check port number supplied by user verbs cmds
  KEYS: Fix an error code in request_master_key()
  ath10k: override CE5 config for QCA9377
  x86/uaccess: Optimize copy_user_enhanced_fast_string() for short strings
  x86/tools: Fix gcc-7 warning in relocs.c
  gfs2: Fix glock rhashtable rcu bug
  USB: serial: qcserial: new Sierra Wireless EM7305 device ID
  USB: serial: option: add two Longcheer device ids
  pinctrl: sh-pfc: Update info pointer after SoC-specific init
  pinctrl: mxs: atomically switch mux and drive strength config
  pinctrl: sunxi: Fix SPDIF function name for A83T
  pinctrl: meson: meson8b: fix the NAND DQS pins
  pinctrl: sh-pfc: r8a7791: Fix SCIF2 pinmux data
  sysctl: report EINVAL if value is larger than UINT_MAX for proc_douintvec
  sysctl: don't print negative flag for proc_douintvec
  mac80211_hwsim: Replace bogus hrtimer clockid
  usb: Fix typo in the definition of Endpoint[out]Request
  usb: usbip: set buffer pointers to NULL after free
  Add USB quirk for HVR-950q to avoid intermittent device resets
  USB: serial: cp210x: add ID for CEL EM3588 USB ZigBee stick
  usb: dwc3: replace %p with %pK
  drm/virtio: don't leak bo on drm_gem_object_init failure
  tracing/kprobes: Allow to create probe with a module name starting with a digit
  mm: fix classzone_idx underflow in shrink_zones()
  bgmac: reset & enable Ethernet core before using it
  driver core: platform: fix race condition with driver_override
  fs: completely ignore unknown open flags
  fs: add a VALID_OPEN_FLAGS
  ANDROID: binder: add RT inheritance flag to node.
  ANDROID: binder: improve priority inheritance.
  ANDROID: binder: add min sched_policy to node.
  ANDROID: binder: add support for RT prio inheritance.
  ANDROID: binder: push new transactions to waiting threads.
  ANDROID: binder: remove proc waitqueue
  FROMLIST: binder: remove global binder lock
  FROMLIST: binder: fix death race conditions
  FROMLIST: binder: protect against stale pointers in print_binder_transaction
  FROMLIST: binder: protect binder_ref with outer lock
  FROMLIST: binder: use inner lock to protect thread accounting
  FROMLIST: binder: protect transaction_stack with inner lock.
  FROMLIST: binder: protect proc->threads with inner_lock
  FROMLIST: binder: protect proc->nodes with inner lock
  FROMLIST: binder: add spinlock to protect binder_node
  FROMLIST: binder: add spinlocks to protect todo lists
  FROMLIST: binder: use inner lock to sync work dq and node counts
  FROMLIST: binder: introduce locking helper functions
  FROMLIST: binder: use node->tmp_refs to ensure node safety
  FROMLIST: binder: refactor binder ref inc/dec for thread safety
  FROMLIST: binder: make sure accesses to proc/thread are safe
  FROMLIST: binder: make sure target_node has strong ref
  FROMLIST: binder: guarantee txn complete / errors delivered in-order
  FROMLIST: binder: refactor binder_pop_transaction
  FROMLIST: binder: use atomic for transaction_log index
  FROMLIST: binder: add more debug info when allocation fails.
  FROMLIST: binder: protect against two threads freeing buffer
  FROMLIST: binder: remove dead code in binder_get_ref_for_node
  FROMLIST: binder: don't modify thread->looper from other threads
  FROMLIST: binder: avoid race conditions when enqueuing txn
  FROMLIST: binder: refactor queue management in binder_thread_read
  FROMLIST: binder: add log information for binder transaction failures
  FROMLIST: binder: make binder_last_id an atomic
  FROMLIST: binder: change binder_stats to atomics
  FROMLIST: binder: add protection for non-perf cases
  FROMLIST: binder: remove binder_debug_no_lock mechanism
  FROMLIST: binder: move binder_alloc to separate file
  FROMLIST: binder: separate out binder_alloc functions
  FROMLIST: binder: remove unneeded cleanup code
  FROMLIST: binder: separate binder allocator structure from binder proc
  FROMLIST: binder: Use wake up hint for synchronous transactions.
  Revert "android: binder: move global binder state into context struct."
  sched: walt: fix window misalignment when HZ=300
  ANDROID: android-base.cfg: remove CONFIG_CGROUP_DEBUG
  ANDROID: sdcardfs: use mount_nodev and fix a issue in sdcardfs_kill_sb

Conflicts:
	drivers/android/binder.c
	drivers/net/wireless/ath/ath10k/pci.c

Change-Id: Ic6f82c2ec9929733a16a03bb3b745187e002f4f6
Signed-off-by: Blagovest Kolenichev <bkolenichev@codeaurora.org>
2017-08-04 07:53:32 -07:00

946 lines
23 KiB
C

/*
* 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;
static 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 inline bool is_unlocked(void)
{
static const char unlocked[] = "orange";
return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked));
}
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 */
static 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");
retval = PTR_ERR(pks);
pks = NULL;
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;
set_disk_ro(dm_disk(dm_table_get_md(ti->table)), 0);
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;
}
static int create_linear_device(struct dm_target *ti, dev_t dev,
char *target_device)
{
u64 device_size = 0;
int 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;
}
/*
* 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 = NULL, *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;
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())
return create_linear_device(ti, dev, target_device);
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) {
/* Allow invalid metadata when the device is unlocked */
if (is_unlocked()) {
DMWARN("Allow invalid metadata when unlocked");
return create_linear_device(ti, dev, target_device);
}
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);