diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk index 8d5b51b87..e321c171a 100644 --- a/fs_mgr/Android.mk +++ b/fs_mgr/Android.mk @@ -11,7 +11,8 @@ common_static_libraries := \ libcrypto \ libext4_utils \ libsquashfs_utils \ - libselinux + libselinux \ + libavb include $(CLEAR_VARS) LOCAL_CLANG := true @@ -22,7 +23,9 @@ LOCAL_SRC_FILES:= \ fs_mgr_format.c \ fs_mgr_fstab.c \ fs_mgr_slotselect.c \ - fs_mgr_verity.cpp + fs_mgr_verity.cpp \ + fs_mgr_avb.cpp \ + fs_mgr_avb_ops.cpp LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ system/vold \ diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index e699b7110..43fb9ea34 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -46,6 +46,7 @@ #include #include "fs_mgr_priv.h" +#include "fs_mgr_priv_avb.h" #include "fs_mgr_priv_verity.h" #define KEY_LOC_PROP "ro.crypto.keyfile.userdata" @@ -670,11 +671,17 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode) int mret = -1; int mount_errno = 0; int attempted_idx = -1; + int avb_ret = FS_MGR_SETUP_AVB_FAIL; if (!fstab) { return -1; } + if (fs_mgr_is_avb_used() && + (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) { + return -1; + } + for (i = 0; i < fstab->num_entries; i++) { /* Don't mount entries that are managed by vold or not for the mount mode*/ if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) || @@ -713,7 +720,22 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode) wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); } - if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { + if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) { + /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we + * should set up the device without using dm-verity. + * The actual mounting still take place in the following + * mount_with_alternatives(). + */ + if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) { + INFO("AVB HASHTREE disabled\n"); + } else if (fs_mgr_setup_avb(&fstab->recs[i]) != + FS_MGR_SETUP_AVB_SUCCESS) { + ERROR("Failed to set up AVB on partition: %s, skipping!\n", + fstab->recs[i].mount_point); + /* Skips mounting the device. */ + continue; + } + } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { int rc = fs_mgr_setup_verity(&fstab->recs[i], true); if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) { INFO("Verity disabled"); @@ -722,6 +744,7 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode) continue; } } + int last_idx_inspected; int top_idx = i; @@ -825,6 +848,10 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode) } } + if (fs_mgr_is_avb_used()) { + fs_mgr_unload_vbmeta_images(); + } + if (error_count) { return -1; } else { @@ -845,11 +872,17 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device, int mount_errors = 0; int first_mount_errno = 0; char *m; + int avb_ret = FS_MGR_SETUP_AVB_FAIL; if (!fstab) { return ret; } + if (fs_mgr_is_avb_used() && + (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) { + return ret; + } + for (i = 0; i < fstab->num_entries; i++) { if (!fs_match(fstab->recs[i].mount_point, n_name)) { continue; @@ -882,7 +915,22 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device, do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i]); } - if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { + if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) { + /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we + * should set up the device without using dm-verity. + * The actual mounting still take place in the following + * mount_with_alternatives(). + */ + if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) { + INFO("AVB HASHTREE disabled\n"); + } else if (fs_mgr_setup_avb(&fstab->recs[i]) != + FS_MGR_SETUP_AVB_SUCCESS) { + ERROR("Failed to set up AVB on partition: %s, skipping!\n", + fstab->recs[i].mount_point); + /* Skips mounting the device. */ + continue; + } + } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { int rc = fs_mgr_setup_verity(&fstab->recs[i], true); if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) { INFO("Verity disabled"); @@ -921,6 +969,9 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device, } out: + if (fs_mgr_is_avb_used()) { + fs_mgr_unload_vbmeta_images(); + } return ret; } diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp new file mode 100644 index 000000000..51632cf39 --- /dev/null +++ b/fs_mgr/fs_mgr_avb.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fs_mgr.h" +#include "fs_mgr_avb_ops.h" +#include "fs_mgr_priv.h" +#include "fs_mgr_priv_avb.h" +#include "fs_mgr_priv_dm_ioctl.h" +#include "fs_mgr_priv_sha.h" + +/* The format of dm-verity construction parameters: + * + * + */ +#define VERITY_TABLE_FORMAT \ + "%u %s %s %u %u " \ + "%" PRIu64 " %" PRIu64 " %s %s %s " + +#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt) \ + hashtree_desc.dm_verity_version, blk_device, blk_device, \ + hashtree_desc.data_block_size, hashtree_desc.hash_block_size, \ + hashtree_desc.image_size / \ + hashtree_desc.data_block_size, /* num_data_blocks. */ \ + hashtree_desc.tree_offset / \ + hashtree_desc.hash_block_size, /* hash_start_block. */ \ + (char *)hashtree_desc.hash_algorithm, digest, salt + +#define VERITY_TABLE_OPT_RESTART "restart_on_corruption" +#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks" + +/* The default format of dm-verity optional parameters: + * <#opt_params> ignore_zero_blocks restart_on_corruption + */ +#define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s" +#define VERITY_TABLE_OPT_DEFAULT_PARAMS \ + VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART + +/* The FEC (forward error correction) format of dm-verity optional parameters: + * <#opt_params> use_fec_from_device + * fec_roots fec_blocks fec_start + * ignore_zero_blocks restart_on_corruption + */ +#define VERITY_TABLE_OPT_FEC_FORMAT \ + "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 \ + " fec_start %" PRIu64 " %s %s" + +/* Note that fec_blocks is the size that FEC covers, *not* the + * size of the FEC data. Since we use FEC for everything up until + * the FEC data, it's the same as the offset (fec_start). + */ +#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device) \ + blk_device, hashtree_desc.fec_num_roots, \ + hashtree_desc.fec_offset / \ + hashtree_desc.data_block_size, /* fec_blocks */ \ + hashtree_desc.fec_offset / \ + hashtree_desc.data_block_size, /* fec_start */ \ + VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART + +AvbSlotVerifyData *fs_mgr_avb_verify_data = nullptr; +AvbOps *fs_mgr_avb_ops = nullptr; + +enum HashAlgorithm { + kInvalid = 0, + kSHA256 = 1, + kSHA512 = 2, +}; + +struct androidboot_vbmeta { + HashAlgorithm hash_alg; + uint8_t digest[SHA512_DIGEST_LENGTH]; + size_t vbmeta_size; + bool allow_verification_error; +}; + +androidboot_vbmeta fs_mgr_vbmeta_prop; + +static inline bool nibble_value(const char &c, uint8_t *value) +{ + FS_MGR_CHECK(value != nullptr); + + switch (c) { + case '0' ... '9': + *value = c - '0'; + break; + case 'a' ... 'f': + *value = c - 'a' + 10; + break; + case 'A' ... 'F': + *value = c - 'A' + 10; + break; + default: + return false; + } + + return true; +} + +static bool hex_to_bytes(uint8_t *bytes, + size_t bytes_len, + const std::string &hex) +{ + FS_MGR_CHECK(bytes != nullptr); + + if (hex.size() % 2 != 0) { + return false; + } + if (hex.size() / 2 > bytes_len) { + return false; + } + for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) { + uint8_t high; + if (!nibble_value(hex[i], &high)) { + return false; + } + uint8_t low; + if (!nibble_value(hex[i + 1], &low)) { + return false; + } + bytes[j] = (high << 4) | low; + } + return true; +} + +static std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len) +{ + FS_MGR_CHECK(bytes != nullptr); + + static const char *hex_digits = "0123456789abcdef"; + std::string hex; + + for (size_t i = 0; i < bytes_len; i++) { + hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]); + hex.push_back(hex_digits[bytes[i] & 0x0F]); + } + return hex; +} + +static bool load_vbmeta_prop(androidboot_vbmeta *vbmeta_prop) +{ + FS_MGR_CHECK(vbmeta_prop != nullptr); + + std::string cmdline; + android::base::ReadFileToString("/proc/cmdline", &cmdline); + + std::string hash_alg; + std::string digest; + + for (const auto &entry : + android::base::Split(android::base::Trim(cmdline), " ")) { + std::vector pieces = android::base::Split(entry, "="); + const std::string &key = pieces[0]; + const std::string &value = pieces[1]; + + if (key == "androidboot.vbmeta.device_state") { + vbmeta_prop->allow_verification_error = (value == "unlocked"); + } else if (key == "androidboot.vbmeta.hash_alg") { + hash_alg = value; + } else if (key == "androidboot.vbmeta.size") { + if (!android::base::ParseUint(value.c_str(), + &vbmeta_prop->vbmeta_size)) { + return false; + } + } else if (key == "androidboot.vbmeta.digest") { + digest = value; + } + } + + // Reads hash algorithm. + size_t expected_digest_size = 0; + if (hash_alg == "sha256") { + expected_digest_size = SHA256_DIGEST_LENGTH * 2; + vbmeta_prop->hash_alg = kSHA256; + } else if (hash_alg == "sha512") { + expected_digest_size = SHA512_DIGEST_LENGTH * 2; + vbmeta_prop->hash_alg = kSHA512; + } else { + ERROR("Unknown hash algorithm: %s\n", hash_alg.c_str()); + return false; + } + + // Reads digest. + if (digest.size() != expected_digest_size) { + ERROR("Unexpected digest size: %zu (expected %zu)\n", digest.size(), + expected_digest_size); + return false; + } + + if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest), + digest)) { + ERROR("Hash digest contains non-hexidecimal character: %s\n", + digest.c_str()); + return false; + } + + return true; +} + +template +static std::pair verify_vbmeta_digest( + const AvbSlotVerifyData &verify_data, const androidboot_vbmeta &vbmeta_prop) +{ + size_t total_size = 0; + Hasher hasher; + for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) { + hasher.update(verify_data.vbmeta_images[n].vbmeta_data, + verify_data.vbmeta_images[n].vbmeta_size); + total_size += verify_data.vbmeta_images[n].vbmeta_size; + } + + bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest, + Hasher::DIGEST_SIZE) == 0); + + return std::make_pair(total_size, matched); +} + +static bool verify_vbmeta_images(const AvbSlotVerifyData &verify_data, + const androidboot_vbmeta &vbmeta_prop) +{ + if (verify_data.num_vbmeta_images == 0) { + ERROR("No vbmeta images\n"); + return false; + } + + size_t total_size = 0; + bool digest_matched = false; + + if (vbmeta_prop.hash_alg == kSHA256) { + std::tie(total_size, digest_matched) = + verify_vbmeta_digest(verify_data, vbmeta_prop); + } else if (vbmeta_prop.hash_alg == kSHA512) { + std::tie(total_size, digest_matched) = + verify_vbmeta_digest(verify_data, vbmeta_prop); + } + + if (total_size != vbmeta_prop.vbmeta_size) { + ERROR("total vbmeta size mismatch: %zu (expected: %zu)\n", total_size, + vbmeta_prop.vbmeta_size); + return false; + } + + if (!digest_matched) { + ERROR("vbmeta digest mismatch\n"); + return false; + } + + return true; +} + +static bool hashtree_load_verity_table( + struct dm_ioctl *io, + const std::string &dm_device_name, + int fd, + const std::string &blk_device, + const AvbHashtreeDescriptor &hashtree_desc, + const std::string &salt, + const std::string &root_digest) +{ + fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG); + + // The buffer consists of [dm_ioctl][dm_target_spec][verity_params]. + char *buffer = (char *)io; + + // Builds the dm_target_spec arguments. + struct dm_target_spec *dm_target = + (struct dm_target_spec *)&buffer[sizeof(struct dm_ioctl)]; + io->target_count = 1; + dm_target->status = 0; + dm_target->sector_start = 0; + dm_target->length = hashtree_desc.image_size / 512; + strcpy(dm_target->target_type, "verity"); + + // Builds the verity params. + char *verity_params = + buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + size_t bufsize = DM_BUF_SIZE - (verity_params - buffer); + + int res = 0; + if (hashtree_desc.fec_size > 0) { + res = snprintf( + verity_params, bufsize, + VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT, + VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), + root_digest.c_str(), salt.c_str()), + VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str())); + } else { + res = snprintf(verity_params, bufsize, + VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT, + VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), + root_digest.c_str(), salt.c_str()), + VERITY_TABLE_OPT_DEFAULT_PARAMS); + } + + if (res < 0 || (size_t)res >= bufsize) { + ERROR("Error building verity table; insufficient buffer size?\n"); + return false; + } + + INFO("loading verity table: '%s'", verity_params); + + // Sets ext target boundary. + verity_params += strlen(verity_params) + 1; + verity_params = (char *)(((unsigned long)verity_params + 7) & ~7); + dm_target->next = verity_params - buffer; + + // Sends the ioctl to load the verity table. + if (ioctl(fd, DM_TABLE_LOAD, io)) { + ERROR("Error loading verity table (%s)\n", strerror(errno)); + return false; + } + + return true; +} + +static bool hashtree_dm_verity_setup(struct fstab_rec *fstab_entry, + const AvbHashtreeDescriptor &hashtree_desc, + const std::string &salt, + const std::string &root_digest) +{ + // Gets the device mapper fd. + android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR)); + if (fd < 0) { + ERROR("Error opening device mapper (%s)\n", strerror(errno)); + return false; + } + + // Creates the device. + alignas(dm_ioctl) char buffer[DM_BUF_SIZE]; + struct dm_ioctl *io = (struct dm_ioctl *)buffer; + const std::string mount_point(basename(fstab_entry->mount_point)); + if (!fs_mgr_create_verity_device(io, mount_point, fd)) { + ERROR("Couldn't create verity device!\n"); + return false; + } + + // Gets the name of the device file. + std::string verity_blk_name; + if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) { + ERROR("Couldn't get verity device number!\n"); + return false; + } + + // Loads the verity mapping table. + if (!hashtree_load_verity_table(io, mount_point, fd, + std::string(fstab_entry->blk_device), + hashtree_desc, salt, root_digest)) { + ERROR("Couldn't load verity table!\n"); + return false; + } + + // Activates the device. + if (!fs_mgr_resume_verity_table(io, mount_point, fd)) { + return false; + } + + // Marks the underlying block device as read-only. + fs_mgr_set_blk_ro(fstab_entry->blk_device); + + // TODO(bowgotsai): support verified all partition at boot. + // Updates fstab_rec->blk_device to verity device name. + free(fstab_entry->blk_device); + fstab_entry->blk_device = strdup(verity_blk_name.c_str()); + + // Makes sure we've set everything up properly. + if (fs_mgr_test_access(verity_blk_name.c_str()) < 0) { + return false; + } + + return true; +} + +static bool get_hashtree_descriptor(const std::string &partition_name, + const AvbSlotVerifyData &verify_data, + AvbHashtreeDescriptor *out_hashtree_desc, + std::string *out_salt, + std::string *out_digest) +{ + bool found = false; + const uint8_t *desc_partition_name; + + for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) { + // Get descriptors from vbmeta_images[i]. + size_t num_descriptors; + std::unique_ptr + descriptors( + avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data, + verify_data.vbmeta_images[i].vbmeta_size, + &num_descriptors), + avb_free); + + if (!descriptors || num_descriptors < 1) { + continue; + } + + // Ensures that hashtree descriptor is either in /vbmeta or in + // the same partition for verity setup. + std::string vbmeta_partition_name( + verify_data.vbmeta_images[i].partition_name); + if (vbmeta_partition_name != "vbmeta" && + vbmeta_partition_name != partition_name) { + WARNING("Skip vbmeta image at %s for partition: %s\n", + verify_data.vbmeta_images[i].partition_name, + partition_name.c_str()); + continue; + } + + for (size_t j = 0; j < num_descriptors && !found; j++) { + AvbDescriptor desc; + if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) { + WARNING("Descriptor is invalid.\n"); + continue; + } + if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) { + desc_partition_name = (const uint8_t *)descriptors[j] + + sizeof(AvbHashtreeDescriptor); + if (!avb_hashtree_descriptor_validate_and_byteswap( + (AvbHashtreeDescriptor *)descriptors[j], + out_hashtree_desc)) { + continue; + } + if (out_hashtree_desc->partition_name_len != + partition_name.length()) { + continue; + } + // Notes that desc_partition_name is not NUL-terminated. + std::string hashtree_partition_name( + (const char *)desc_partition_name, + out_hashtree_desc->partition_name_len); + if (hashtree_partition_name == partition_name) { + found = true; + } + } + } + } + + if (!found) { + ERROR("Partition descriptor not found: %s\n", partition_name.c_str()); + return false; + } + + const uint8_t *desc_salt = + desc_partition_name + out_hashtree_desc->partition_name_len; + *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len); + + const uint8_t *desc_digest = desc_salt + out_hashtree_desc->salt_len; + *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len); + + return true; +} + +static inline bool polling_vbmeta_blk_device(struct fstab *fstab) +{ + // It needs the block device symlink: fstab_rec->blk_device to read + // /vbmeta partition. However, the symlink created by ueventd might + // not be ready at this point. Use test_access() to poll it before + // trying to read the partition. + struct fstab_rec *fstab_entry = + fs_mgr_get_entry_for_mount_point(fstab, "/vbmeta"); + + // Makes sure /vbmeta block device is ready to access. + if (fs_mgr_test_access(fstab_entry->blk_device) < 0) { + return false; + } + return true; +} + +static bool init_is_avb_used() +{ + // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg, + // size, digest} in kernel cmdline. They will then be imported by init + // process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}. + // + // Checks hash_alg as an indicator for whether AVB is used. + // We don't have to parse and check all of them here. The check will + // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will + // be returned when there is an error. + + std::string hash_alg = + android::base::GetProperty("ro.boot.vbmeta.hash_alg", ""); + + if (hash_alg == "sha256" || hash_alg == "sha512") { + return true; + } + + return false; +} + +bool fs_mgr_is_avb_used() +{ + static bool result = init_is_avb_used(); + return result; +} + +int fs_mgr_load_vbmeta_images(struct fstab *fstab) +{ + FS_MGR_CHECK(fstab != nullptr); + + if (!polling_vbmeta_blk_device(fstab)) { + ERROR("Failed to find block device of /vbmeta\n"); + return FS_MGR_SETUP_AVB_FAIL; + } + + // Gets the expected hash value of vbmeta images from + // kernel cmdline. + if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) { + return FS_MGR_SETUP_AVB_FAIL; + } + + fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab); + if (fs_mgr_avb_ops == nullptr) { + ERROR("Failed to allocate dummy avb_ops\n"); + return FS_MGR_SETUP_AVB_FAIL; + } + + // Invokes avb_slot_verify() to load and verify all vbmeta images. + // Sets requested_partitions to nullptr as it's to copy the contents + // of HASH partitions into fs_mgr_avb_verify_data, which is not required as + // fs_mgr only deals with HASHTREE partitions. + const char *requested_partitions[] = {nullptr}; + const char *ab_suffix = + android::base::GetProperty("ro.boot.slot_suffix", "").c_str(); + AvbSlotVerifyResult verify_result = avb_slot_verify( + fs_mgr_avb_ops, requested_partitions, ab_suffix, + fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data); + + // Only allow two verify results: + // - AVB_SLOT_VERIFY_RESULT_OK. + // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state). + if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) { + if (!fs_mgr_vbmeta_prop.allow_verification_error) { + ERROR("ERROR_VERIFICATION isn't allowed\n"); + goto fail; + } + } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) { + ERROR("avb_slot_verify failed, result: %d\n", verify_result); + goto fail; + } + + // Verifies vbmeta images against the digest passed from bootloader. + if (!verify_vbmeta_images(*fs_mgr_avb_verify_data, fs_mgr_vbmeta_prop)) { + ERROR("verify_vbmeta_images failed\n"); + goto fail; + } else { + // Checks whether FLAGS_HASHTREE_DISABLED is set. + AvbVBMetaImageHeader vbmeta_header; + avb_vbmeta_image_header_to_host_byte_order( + (AvbVBMetaImageHeader *)fs_mgr_avb_verify_data->vbmeta_images[0] + .vbmeta_data, + &vbmeta_header); + + bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags & + AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED); + if (hashtree_disabled) { + return FS_MGR_SETUP_AVB_HASHTREE_DISABLED; + } + } + + if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) { + return FS_MGR_SETUP_AVB_SUCCESS; + } + +fail: + fs_mgr_unload_vbmeta_images(); + return FS_MGR_SETUP_AVB_FAIL; +} + +void fs_mgr_unload_vbmeta_images() +{ + if (fs_mgr_avb_verify_data != nullptr) { + avb_slot_verify_data_free(fs_mgr_avb_verify_data); + } + + if (fs_mgr_avb_ops != nullptr) { + fs_mgr_dummy_avb_ops_free(fs_mgr_avb_ops); + } +} + +int fs_mgr_setup_avb(struct fstab_rec *fstab_entry) +{ + if (!fstab_entry || !fs_mgr_avb_verify_data || + fs_mgr_avb_verify_data->num_vbmeta_images < 1) { + return FS_MGR_SETUP_AVB_FAIL; + } + + std::string partition_name(basename(fstab_entry->mount_point)); + if (!avb_validate_utf8((const uint8_t *)partition_name.c_str(), + partition_name.length())) { + ERROR("Partition name: %s is not valid UTF-8.\n", + partition_name.c_str()); + return FS_MGR_SETUP_AVB_FAIL; + } + + AvbHashtreeDescriptor hashtree_descriptor; + std::string salt; + std::string root_digest; + if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data, + &hashtree_descriptor, &salt, &root_digest)) { + return FS_MGR_SETUP_AVB_FAIL; + } + + // Converts HASHTREE descriptor to verity_table_params. + if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, + root_digest)) { + return FS_MGR_SETUP_AVB_FAIL; + } + + return FS_MGR_SETUP_AVB_SUCCESS; +} diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp new file mode 100644 index 000000000..f3030eb96 --- /dev/null +++ b/fs_mgr/fs_mgr_avb_ops.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fs_mgr.h" +#include "fs_mgr_avb_ops.h" +#include "fs_mgr_priv.h" + +static struct fstab *fs_mgr_fstab = nullptr; + +static AvbIOResult read_from_partition(AvbOps *ops ATTRIBUTE_UNUSED, + const char *partition, + int64_t offset, + size_t num_bytes, + void *buffer, + size_t *out_num_read) +{ + // The input |partition| name is with ab_suffix, e.g. system_a. + // Slot suffix (e.g. _a) will be appended to the device file path + // for partitions having 'slotselect' optin in fstab file, but it + // won't be appended to the mount point. + // + // In AVB, we can assume that there's an entry for the /misc mount + // point and use that to get the device file for the misc partition. + // From there we'll assume that a by-name scheme is used + // so we can just replace the trailing "misc" by the given + // |partition|, e.g. + // + // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc -> + // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a + + struct fstab_rec *fstab_entry = + fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc"); + + if (fstab_entry == nullptr) { + ERROR("Partition (%s) not found in fstab\n", partition); + return AVB_IO_RESULT_ERROR_IO; + } + + std::string partition_name(partition); + std::string path(fstab_entry->blk_device); + // Replaces the last field of device file if it's not misc. + if (!android::base::StartsWith(partition_name, "misc")) { + size_t end_slash = path.find_last_of("/"); + std::string by_name_prefix(path.substr(0, end_slash + 1)); + path = by_name_prefix + partition_name; + } + + android::base::unique_fd fd( + TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + + if (fd < 0) { + ERROR("Failed to open %s (%s)\n", path.c_str(), strerror(errno)); + return AVB_IO_RESULT_ERROR_IO; + } + + // If offset is negative, interprets its absolute value as the + // number of bytes from the end of the partition. + if (offset < 0) { + off64_t total_size = lseek64(fd, 0, SEEK_END); + if (total_size == -1) { + ERROR("Failed to lseek64 to end of the partition\n"); + return AVB_IO_RESULT_ERROR_IO; + } + offset = total_size + offset; + // Repositions the offset to the beginning. + if (lseek64(fd, 0, SEEK_SET) == -1) { + ERROR("Failed to lseek64 to the beginning of the partition\n"); + return AVB_IO_RESULT_ERROR_IO; + } + } + + // On Linux, we never get partial reads from block devices (except + // for EOF). + ssize_t num_read = + TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset)); + + if (num_read < 0 || (size_t)num_read != num_bytes) { + ERROR("Failed to read %zu bytes from %s offset %" PRId64 " (%s)\n", + num_bytes, path.c_str(), offset, strerror(errno)); + return AVB_IO_RESULT_ERROR_IO; + } + + if (out_num_read != nullptr) { + *out_num_read = num_read; + } + + return AVB_IO_RESULT_OK; +} + +static AvbIOResult dummy_read_rollback_index(AvbOps *ops ATTRIBUTE_UNUSED, + size_t rollback_index_location + ATTRIBUTE_UNUSED, + uint64_t *out_rollback_index) +{ + // rollback_index has been checked in bootloader phase. + // In user-space, returns the smallest value 0 to pass the check. + *out_rollback_index = 0; + return AVB_IO_RESULT_OK; +} + +static AvbIOResult dummy_validate_vbmeta_public_key( + AvbOps *ops ATTRIBUTE_UNUSED, + const uint8_t *public_key_data ATTRIBUTE_UNUSED, + size_t public_key_length ATTRIBUTE_UNUSED, + const uint8_t *public_key_metadata ATTRIBUTE_UNUSED, + size_t public_key_metadata_length ATTRIBUTE_UNUSED, + bool *out_is_trusted) +{ + // vbmeta public key has been checked in bootloader phase. + // In user-space, returns true to pass the check. + // + // Addtionally, user-space should check + // androidboot.vbmeta.{hash_alg, size, digest} against the digest + // of all vbmeta images after invoking avb_slot_verify(). + + *out_is_trusted = true; + return AVB_IO_RESULT_OK; +} + +static AvbIOResult dummy_read_is_device_unlocked(AvbOps *ops ATTRIBUTE_UNUSED, + bool *out_is_unlocked) +{ + // The function is for bootloader to update the value into + // androidboot.vbmeta.device_state in kernel cmdline. + // In user-space, returns true as we don't need to update it anymore. + *out_is_unlocked = true; + return AVB_IO_RESULT_OK; +} + +static AvbIOResult dummy_get_unique_guid_for_partition( + AvbOps *ops ATTRIBUTE_UNUSED, + const char *partition ATTRIBUTE_UNUSED, + char *guid_buf, + size_t guid_buf_size) +{ + // The function is for bootloader to set the correct UUID + // for a given partition in kernel cmdline. + // In user-space, returns a faking one as we don't need to update + // it anymore. + snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition); + return AVB_IO_RESULT_OK; +} + +AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab) +{ + AvbOps *ops; + + // Assigns the fstab to the static variable for later use. + fs_mgr_fstab = fstab; + + ops = (AvbOps *)calloc(1, sizeof(AvbOps)); + if (ops == nullptr) { + ERROR("Error allocating memory for AvbOps.\n"); + return nullptr; + } + + // We only need these operations since that's all what is being used + // by the avb_slot_verify(); Most of them are dummy operations because + // they're only required in bootloader but not required in user-space. + ops->read_from_partition = read_from_partition; + ops->read_rollback_index = dummy_read_rollback_index; + ops->validate_vbmeta_public_key = dummy_validate_vbmeta_public_key; + ops->read_is_device_unlocked = dummy_read_is_device_unlocked; + ops->get_unique_guid_for_partition = dummy_get_unique_guid_for_partition; + + return ops; +} + +void fs_mgr_dummy_avb_ops_free(AvbOps *ops) +{ + free(ops); +} diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_avb_ops.h new file mode 100644 index 000000000..9f99be87f --- /dev/null +++ b/fs_mgr/fs_mgr_avb_ops.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __CORE_FS_MGR_AVB_OPS_H +#define __CORE_FS_MGR_AVB_OPS_H + +#include + +#include "fs_mgr.h" + +__BEGIN_DECLS + +/* Allocates a "dummy" AvbOps instance solely for use in user-space. + * Returns nullptr on OOM. + * + * It mainly provides read_from_partitions() for user-space to get + * AvbSlotVerifyData.vbmeta_images[] and the caller MUST check their + * integrity against the androidboot.vbmeta.{hash_alg, size, digest} + * values from /proc/cmdline, e.g. verify_vbmeta_images() + * in fs_mgr_avb.cpp. + * + * Other I/O operations are only required in boot loader so we set + * them as dummy operations here. + * - Will allow any public key for signing. + * - returns 0 for any rollback index location. + * - returns device is unlocked regardless of the actual state. + * - returns a dummy guid for any partition. + * + * Frees with fs_mgr_dummy_avb_ops_free(). + */ +AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab); + +/* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */ +void fs_mgr_dummy_avb_ops_free(AvbOps *ops); + +__END_DECLS + +#endif /* __CORE_FS_MGR_AVB_OPS_H */ diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp index 75ce621b3..939657e54 100644 --- a/fs_mgr/fs_mgr_dm_ioctl.cpp +++ b/fs_mgr/fs_mgr_dm_ioctl.cpp @@ -68,7 +68,7 @@ bool fs_mgr_get_verity_device_name(struct dm_ioctl *io, int fd, std::string *out_dev_name) { - CHECK(out_dev_name != nullptr); + FS_MGR_CHECK(out_dev_name != nullptr); fs_mgr_verity_ioctl_init(io, name, 0); if (ioctl(fd, DM_DEV_STATUS, io)) { diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index 5d43b6c6b..b9d561790 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -79,6 +79,7 @@ static struct flag_list fs_mgr_flags[] = { { "max_comp_streams=", MF_MAX_COMP_STREAMS }, { "verifyatboot", MF_VERIFYATBOOT }, { "verify", MF_VERIFY }, + { "avb", MF_AVB }, { "noemulatedsd", MF_NOEMULATEDSD }, { "notrim", MF_NOTRIM }, { "formattable", MF_FORMATTABLE }, diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 074cda67b..0a27c7a3f 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -20,6 +20,17 @@ #include #include +#ifdef __cplusplus +#include +/* The CHECK() in logging.h will use program invocation name as the tag. + * Thus, the log will have prefix "init: " when libfs_mgr is statically + * linked in the init process. This might be opaque when debugging. + * Appends "in libfs_mgr" at the end of the abort message to explicitly + * indicate the check happens in fs_mgr. + */ +#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr " +#endif + __BEGIN_DECLS #define INFO(x...) KLOG_INFO("fs_mgr", x) @@ -91,6 +102,7 @@ __BEGIN_DECLS #define MF_QUOTA 0x400000 #define MF_ERASEBLKSIZE 0x800000 #define MF_LOGICALBLKSIZE 0X1000000 +#define MF_AVB 0X2000000 #define DM_BUF_SIZE 4096 diff --git a/fs_mgr/fs_mgr_priv_avb.h b/fs_mgr/fs_mgr_priv_avb.h new file mode 100644 index 000000000..6d0171ca8 --- /dev/null +++ b/fs_mgr/fs_mgr_priv_avb.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CORE_FS_MGR_PRIV_AVB_H +#define __CORE_FS_MGR_PRIV_AVB_H + +#ifndef __cplusplus +#include +#endif + +#include "fs_mgr.h" + +__BEGIN_DECLS + +#define FS_MGR_SETUP_AVB_HASHTREE_DISABLED (-2) +#define FS_MGR_SETUP_AVB_FAIL (-1) +#define FS_MGR_SETUP_AVB_SUCCESS 0 + +bool fs_mgr_is_avb_used(); + +/* Gets AVB metadata through external/avb/libavb for all partitions: + * AvbSlotVerifyData.vbmeta_images[] and checks their integrity + * against the androidboot.vbmeta.{hash_alg, size, digest} values + * from /proc/cmdline. + * + * Return values: + * - FS_MGR_SETUP_AVB_SUCCESS: the metadata cab be trusted. + * - FS_MGR_SETUP_AVB_FAIL: any error when reading and verifying the + * metadata, e.g. I/O error, digest value mismatch, size mismatch. + * - FS_MGR_SETUP_AVB_HASHTREE_DISABLED: to support the existing + * 'adb disable-verity' feature in Android. It's very helpful for + * developers to make the filesystem writable to allow replacing + * binaries on the device. + */ +int fs_mgr_load_vbmeta_images(struct fstab *fstab); + +void fs_mgr_unload_vbmeta_images(); + +int fs_mgr_setup_avb(struct fstab_rec *fstab_entry); + +__END_DECLS + +#endif /* __CORE_FS_MGR_PRIV_AVB_H */ diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h new file mode 100644 index 000000000..1abc27311 --- /dev/null +++ b/fs_mgr/fs_mgr_priv_sha.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CORE_FS_MGR_PRIV_SHA_H +#define __CORE_FS_MGR_PRIV_SHA_H + +#include + +class SHA256Hasher +{ + private: + SHA256_CTX sha256_ctx; + uint8_t hash[SHA256_DIGEST_LENGTH]; + + public: + enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH }; + + SHA256Hasher() + { + SHA256_Init(&sha256_ctx); + } + + void update(const void *data, size_t data_size) + { + SHA256_Update(&sha256_ctx, data, data_size); + } + + const uint8_t *finalize() + { + SHA256_Final(hash, &sha256_ctx); + return hash; + } +}; + +class SHA512Hasher +{ + private: + SHA512_CTX sha512_ctx; + uint8_t hash[SHA512_DIGEST_LENGTH]; + + public: + enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH }; + + SHA512Hasher() + { + SHA512_Init(&sha512_ctx); + } + + void update(const uint8_t *data, size_t data_size) + { + SHA512_Update(&sha512_ctx, data, data_size); + } + + const uint8_t *finalize() + { + SHA512_Final(hash, &sha512_ctx); + return hash; + } +}; + +#endif /* __CORE_FS_MGR_PRIV_SHA_H */ diff --git a/init/Android.mk b/init/Android.mk index 35e6f4fb1..759be52ac 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -107,6 +107,7 @@ LOCAL_STATIC_LIBRARIES := \ libz \ libprocessgroup \ libnl \ + libavb # Create symlinks. LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \