diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index 87db98b0877d..04f046accd97 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -68,6 +68,7 @@ cc_defaults { "file_wait.cpp", "fs_mgr.cpp", "fs_mgr_format.cpp", + "fs_mgr_verity.cpp", "fs_mgr_dm_linear.cpp", "fs_mgr_roots.cpp", "fs_mgr_overlayfs_control.cpp", diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index af2b35a2f11e..780ffe4eee26 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp @@ -1514,6 +1514,14 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) { // Skips mounting the device. continue; } + } else if ((current_entry.fs_mgr_flags.verify)) { + int rc = fs_mgr_setup_verity(¤t_entry, true); + if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) { + LINFO << "Verity disabled"; + } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) { + LERROR << "Could not set up verified partition, skipping!"; + continue; + } } int last_idx_inspected; @@ -1677,6 +1685,13 @@ int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) { ret |= FsMgrUmountStatus::ERROR_VERITY; continue; } + } else if ((current_entry.fs_mgr_flags.verify)) { + if (!fs_mgr_teardown_verity(¤t_entry)) { + LERROR << "Failed to tear down verified partition on mount point: " + << current_entry.mount_point; + ret |= FsMgrUmountStatus::ERROR_VERITY; + continue; + } } } return ret; @@ -1966,6 +1981,14 @@ int fs_mgr_do_mount(Fstab* fstab, const std::string& n_name, const std::string& // Skips mounting the device. continue; } + } else if (fstab_entry.fs_mgr_flags.verify) { + int rc = fs_mgr_setup_verity(&fstab_entry, true); + if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) { + LINFO << "Verity disabled"; + } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) { + LERROR << "Could not set up verified partition, skipping!"; + continue; + } } int retry_count = 2; @@ -2156,7 +2179,7 @@ bool fs_mgr_swapon_all(const Fstab& fstab) { } bool fs_mgr_is_verity_enabled(const FstabEntry& entry) { - if (!entry.fs_mgr_flags.avb) { + if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) { return false; } @@ -2167,12 +2190,17 @@ bool fs_mgr_is_verity_enabled(const FstabEntry& entry) { return false; } + const char* status; std::vector table; if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) { - return false; + if (!entry.fs_mgr_flags.verify_at_boot) { + return false; + } + status = "V"; + } else { + status = table[0].data.c_str(); } - auto status = table[0].data.c_str(); if (*status == 'C' || *status == 'V') { return true; } @@ -2181,7 +2209,7 @@ bool fs_mgr_is_verity_enabled(const FstabEntry& entry) { } std::optional fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) { - if (!entry.fs_mgr_flags.avb) { + if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) { return {}; } DeviceMapper& dm = DeviceMapper::Instance(); @@ -2340,25 +2368,6 @@ bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) { return true; } -bool fs_mgr_load_verity_state(int* mode) { - // unless otherwise specified, use EIO mode. - *mode = VERITY_MODE_EIO; - - // The bootloader communicates verity mode via the kernel commandline - std::string verity_mode; - if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) { - return false; - } - - if (verity_mode == "enforcing") { - *mode = VERITY_MODE_DEFAULT; - } else if (verity_mode == "logging") { - *mode = VERITY_MODE_LOGGING; - } - - return true; -} - bool fs_mgr_filesystem_available(const std::string& filesystem) { std::string filesystems; if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false; diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 7e4d5e54db4c..6eea57fb9d64 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -89,6 +89,8 @@ bool fs_mgr_is_f2fs(const std::string& blk_device); bool fs_mgr_filesystem_available(const std::string& filesystem); std::string fs_mgr_get_context(const std::string& mount_point); +bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab); + namespace android { namespace fs_mgr { diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp new file mode 100644 index 000000000000..efa2180787dd --- /dev/null +++ b/fs_mgr/fs_mgr_verity.cpp @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2013 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 +#include +#include +#include +#include +#include + +#include "fec/io.h" + +#include "fs_mgr.h" +#include "fs_mgr_dm_linear.h" +#include "fs_mgr_priv.h" + +// Realistically, this file should be part of the android::fs_mgr namespace; +using namespace android::fs_mgr; + +#define VERITY_TABLE_RSA_KEY "/verity_key" +#define VERITY_TABLE_HASH_IDX 8 +#define VERITY_TABLE_SALT_IDX 9 + +#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 %" PRIu64 " fec_blocks %" PRIu64 \ + " fec_roots %u " VERITY_TABLE_OPT_IGNZERO +#define VERITY_TABLE_OPT_FEC_ARGS 9 + +#define METADATA_MAGIC 0x01564c54 +#define METADATA_TAG_MAX_LENGTH 63 +#define METADATA_EOD "eod" + +#define VERITY_LASTSIG_TAG "verity_lastsig" + +#define VERITY_STATE_TAG "verity_state" +#define VERITY_STATE_HEADER 0x83c0ae9d +#define VERITY_STATE_VERSION 1 + +#define VERITY_KMSG_RESTART "dm-verity device corrupted" +#define VERITY_KMSG_BUFSIZE 1024 + +#define READ_BUF_SIZE 4096 + +#define __STRINGIFY(x) #x +#define STRINGIFY(x) __STRINGIFY(x) + +struct verity_state { + uint32_t header; + uint32_t version; + int32_t mode; +}; + +extern struct fs_info info; + +static RSA *load_key(const char *path) +{ + uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE]; + + auto f = std::unique_ptr{fopen(path, "re"), fclose}; + if (!f) { + LERROR << "Can't open " << path; + return nullptr; + } + + if (!fread(key_data, sizeof(key_data), 1, f.get())) { + LERROR << "Could not read key!"; + return nullptr; + } + + RSA* key = nullptr; + if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) { + LERROR << "Could not parse key!"; + return nullptr; + } + + return key; +} + +static int verify_table(const uint8_t *signature, size_t signature_size, + const char *table, uint32_t table_length) +{ + RSA *key; + uint8_t hash_buf[SHA256_DIGEST_LENGTH]; + int retval = -1; + + // Hash the table + SHA256((uint8_t*)table, table_length, hash_buf); + + // Now get the public key from the keyfile + key = load_key(VERITY_TABLE_RSA_KEY); + if (!key) { + LERROR << "Couldn't load verity keys"; + goto out; + } + + // verify the result + if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature, + signature_size, key)) { + LERROR << "Couldn't verify table"; + goto out; + } + + retval = 0; + +out: + RSA_free(key); + return retval; +} + +static int verify_verity_signature(const struct fec_verity_metadata& verity) +{ + if (verify_table(verity.signature, sizeof(verity.signature), + verity.table, verity.table_length) == 0 || + verify_table(verity.ecc_signature, sizeof(verity.ecc_signature), + verity.table, verity.table_length) == 0) { + return 0; + } + + return -1; +} + +static int invalidate_table(char *table, size_t table_length) +{ + size_t n = 0; + size_t idx = 0; + size_t cleared = 0; + + while (n < table_length) { + if (table[n++] == ' ') { + ++idx; + } + + if (idx != VERITY_TABLE_HASH_IDX && idx != VERITY_TABLE_SALT_IDX) { + continue; + } + + while (n < table_length && table[n] != ' ') { + table[n++] = '0'; + } + + if (++cleared == 2) { + return 0; + } + } + + return -1; +} + +struct verity_table_params { + char *table; + int mode; + struct fec_ecc_metadata ecc; + const char *ecc_dev; +}; + +typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize, + const struct verity_table_params *params); + +static bool format_verity_table(char *buf, const size_t bufsize, + const struct verity_table_params *params) +{ + const char *mode_flag = NULL; + int res = -1; + + if (params->mode == VERITY_MODE_RESTART) { + mode_flag = VERITY_TABLE_OPT_RESTART; + } else if (params->mode == VERITY_MODE_LOGGING) { + mode_flag = VERITY_TABLE_OPT_LOGGING; + } + + if (params->ecc.valid) { + if (mode_flag) { + res = snprintf(buf, bufsize, + "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT, + params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev, + params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots); + } else { + res = snprintf(buf, bufsize, + "%s %u " VERITY_TABLE_OPT_FEC_FORMAT, + params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev, + params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots); + } + } else if (mode_flag) { + res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table, + mode_flag); + } else { + res = snprintf(buf, bufsize, "%s 1 " VERITY_TABLE_OPT_IGNZERO, params->table); + } + + if (res < 0 || (size_t)res >= bufsize) { + LERROR << "Error building verity table; insufficient buffer size?"; + return false; + } + + return true; +} + +static bool format_legacy_verity_table(char *buf, const size_t bufsize, + const struct verity_table_params *params) +{ + int res; + + if (params->mode == VERITY_MODE_EIO) { + res = strlcpy(buf, params->table, bufsize); + } else { + res = snprintf(buf, bufsize, "%s %d", params->table, params->mode); + } + + if (res < 0 || (size_t)res >= bufsize) { + LERROR << "Error building verity table; insufficient buffer size?"; + return false; + } + + return true; +} + +static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name, + uint64_t device_size, const struct verity_table_params* params, + format_verity_table_func format) { + android::dm::DmTable table; + table.set_readonly(true); + + char buffer[DM_BUF_SIZE]; + if (!format(buffer, sizeof(buffer), params)) { + LERROR << "Failed to format verity parameters"; + return -1; + } + + android::dm::DmTargetVerityString target(0, device_size / 512, buffer); + if (!table.AddTarget(std::make_unique(target))) { + LERROR << "Failed to add verity target"; + return -1; + } + if (!dm.CreateDevice(name, table)) { + LERROR << "Failed to create verity device \"" << name << "\""; + return -1; + } + return 0; +} + +static int read_partition(const char *path, uint64_t size) +{ + char buf[READ_BUF_SIZE]; + ssize_t size_read; + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))); + + if (fd == -1) { + PERROR << "Failed to open " << path; + return -errno; + } + + while (size) { + size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE)); + if (size_read == -1) { + PERROR << "Error in reading partition " << path; + return -errno; + } + size -= size_read; + } + + return 0; +} + +bool fs_mgr_load_verity_state(int* mode) { + // unless otherwise specified, use EIO mode. + *mode = VERITY_MODE_EIO; + + // The bootloader communicates verity mode via the kernel commandline + std::string verity_mode; + if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) { + return false; + } + + if (verity_mode == "enforcing") { + *mode = VERITY_MODE_DEFAULT; + } else if (verity_mode == "logging") { + *mode = VERITY_MODE_LOGGING; + } + + return true; +} + +// Update the verity table using the actual block device path. +// Two cases: +// Case-1: verity table is shared for devices with different by-name prefix. +// Example: +// verity table token: /dev/block/bootdevice/by-name/vendor +// blk_device-1 (non-A/B): /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor +// blk_device-2 (A/B): /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a +// +// Case-2: append A/B suffix in the verity table. +// Example: +// verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor +// blk_device: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a +static void update_verity_table_blk_device(const std::string& blk_device, char** table, + bool slot_select) { + bool updated = false; + std::string result, ab_suffix; + auto tokens = android::base::Split(*table, " "); + + // If slot_select is set, it means blk_device is already updated with ab_suffix. + if (slot_select) ab_suffix = fs_mgr_get_slot_suffix(); + + for (const auto& token : tokens) { + std::string new_token; + if (android::base::StartsWith(token, "/dev/block/")) { + if (token == blk_device) return; // no need to update if they're already the same. + std::size_t found1 = blk_device.find("by-name"); + std::size_t found2 = token.find("by-name"); + if (found1 != std::string::npos && found2 != std::string::npos && + blk_device.substr(found1) == token.substr(found2) + ab_suffix) { + new_token = blk_device; + } + } + + if (!new_token.empty()) { + updated = true; + LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token + << "'"; + } else { + new_token = token; + } + + if (result.empty()) { + result = new_token; + } else { + result += " " + new_token; + } + } + + if (!updated) { + return; + } + + free(*table); + *table = strdup(result.c_str()); +} + +// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for +// mount. The 'wait_for_verity_dev' parameter makes this function wait for the +// verity device to get created before return +int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) { + int retval = FS_MGR_SETUP_VERITY_FAIL; + int fd = -1; + std::string verity_blk_name; + struct fec_handle *f = NULL; + struct fec_verity_metadata verity; + struct verity_table_params params = { .table = NULL }; + + const std::string mount_point(basename(entry->mount_point.c_str())); + bool verified_at_boot = false; + + android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance(); + + if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) < + 0) { + PERROR << "Failed to open '" << entry->blk_device << "'"; + return retval; + } + + // read verity metadata + if (fec_verity_get_metadata(f, &verity) < 0) { + PERROR << "Failed to get verity metadata '" << entry->blk_device << "'"; + // Allow verity disabled when the device is unlocked without metadata + if (fs_mgr_is_device_unlocked()) { + retval = FS_MGR_SETUP_VERITY_SKIPPED; + LWARNING << "Allow invalid metadata when the device is unlocked"; + } + goto out; + } + +#ifdef ALLOW_ADBD_DISABLE_VERITY + if (verity.disabled) { + retval = FS_MGR_SETUP_VERITY_DISABLED; + LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG"; + goto out; + } +#endif + + // read ecc metadata + if (fec_ecc_get_metadata(f, ¶ms.ecc) < 0) { + params.ecc.valid = false; + } + + params.ecc_dev = entry->blk_device.c_str(); + + if (!fs_mgr_load_verity_state(¶ms.mode)) { + /* if accessing or updating the state failed, switch to the default + * safe mode. This makes sure the device won't end up in an endless + * restart loop, and no corrupted data will be exposed to userspace + * without a warning. */ + params.mode = VERITY_MODE_EIO; + } + + if (!verity.table) { + goto out; + } + + params.table = strdup(verity.table); + if (!params.table) { + goto out; + } + + // verify the signature on the table + if (verify_verity_signature(verity) < 0) { + // Allow signature verification error when the device is unlocked + if (fs_mgr_is_device_unlocked()) { + retval = FS_MGR_SETUP_VERITY_SKIPPED; + LWARNING << "Allow signature verification error when the device is unlocked"; + goto out; + } + if (params.mode == VERITY_MODE_LOGGING) { + // the user has been warned, allow mounting without dm-verity + retval = FS_MGR_SETUP_VERITY_SKIPPED; + goto out; + } + + // invalidate root hash and salt to trigger device-specific recovery + if (invalidate_table(params.table, verity.table_length) < 0) { + goto out; + } + } + + LINFO << "Enabling dm-verity for " << mount_point.c_str() + << " (mode " << params.mode << ")"; + + // Update the verity params using the actual block device path + update_verity_table_blk_device(entry->blk_device, ¶ms.table, + entry->fs_mgr_flags.slot_select); + + // load the verity mapping table + if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) { + goto loaded; + } + + if (params.ecc.valid) { + // kernel may not support error correction, try without + LINFO << "Disabling error correction for " << mount_point.c_str(); + params.ecc.valid = false; + + if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) { + goto loaded; + } + } + + // try the legacy format for backwards compatibility + if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_legacy_verity_table) == + 0) { + goto loaded; + } + + if (params.mode != VERITY_MODE_EIO) { + // as a last resort, EIO mode should always be supported + LINFO << "Falling back to EIO mode for " << mount_point.c_str(); + params.mode = VERITY_MODE_EIO; + + if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, + format_legacy_verity_table) == 0) { + goto loaded; + } + } + + LERROR << "Failed to load verity table for " << mount_point.c_str(); + goto out; + +loaded: + if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) { + LERROR << "Couldn't get verity device number!"; + goto out; + } + + // mark the underlying block device as read-only + fs_mgr_set_blk_ro(entry->blk_device); + + // Verify the entire partition in one go + // If there is an error, allow it to mount as a normal verity partition. + if (entry->fs_mgr_flags.verify_at_boot) { + LINFO << "Verifying partition " << entry->blk_device << " at boot"; + int err = read_partition(verity_blk_name.c_str(), verity.data_size); + if (!err) { + LINFO << "Verified verity partition " << entry->blk_device << " at boot"; + verified_at_boot = true; + } + } + + // assign the new verity block device as the block device + if (!verified_at_boot) { + entry->blk_device = verity_blk_name; + } else if (!dm.DeleteDevice(mount_point)) { + LERROR << "Failed to remove verity device " << mount_point.c_str(); + goto out; + } + + // make sure we've set everything up properly + if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) { + goto out; + } + + retval = FS_MGR_SETUP_VERITY_SUCCESS; + +out: + if (fd != -1) { + close(fd); + } + + fec_close(f); + free(params.table); + + return retval; +} + +bool fs_mgr_teardown_verity(FstabEntry* entry) { + const std::string mount_point(basename(entry->mount_point.c_str())); + if (!android::fs_mgr::UnmapDevice(mount_point)) { + return false; + } + LINFO << "Unmapped verity device " << mount_point; + return true; +} diff --git a/fs_mgr/libfstab/fstab.cpp b/fs_mgr/libfstab/fstab.cpp index 443017a2640c..9446b11d9ee9 100644 --- a/fs_mgr/libfstab/fstab.cpp +++ b/fs_mgr/libfstab/fstab.cpp @@ -168,10 +168,12 @@ bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) { CheckFlag("recoveryonly", recovery_only); CheckFlag("noemulatedsd", no_emulated_sd); CheckFlag("notrim", no_trim); + CheckFlag("verify", verify); CheckFlag("formattable", formattable); CheckFlag("slotselect", slot_select); CheckFlag("latemount", late_mount); CheckFlag("nofail", no_fail); + CheckFlag("verifyatboot", verify_at_boot); CheckFlag("quota", quota); CheckFlag("avb", avb); CheckFlag("logical", logical); diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h index 09471f087f6c..b42577674656 100644 --- a/fs_mgr/libfstab/include/fstab/fstab.h +++ b/fs_mgr/libfstab/include/fstab/fstab.h @@ -65,6 +65,7 @@ struct FstabEntry { bool nonremovable : 1; bool vold_managed : 1; bool recovery_only : 1; + bool verify : 1; bool no_emulated_sd : 1; // No emulated sdcard daemon; sd card is the only external // storage. bool no_trim : 1; @@ -73,6 +74,7 @@ struct FstabEntry { bool slot_select : 1; bool late_mount : 1; bool no_fail : 1; + bool verify_at_boot : 1; bool quota : 1; bool avb : 1; bool logical : 1; diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp index 322bf1b08930..36055e025874 100644 --- a/fs_mgr/tests/fs_mgr_test.cpp +++ b/fs_mgr/tests/fs_mgr_test.cpp @@ -204,6 +204,7 @@ bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) { lhs.nonremovable == rhs.nonremovable && lhs.vold_managed == rhs.vold_managed && lhs.recovery_only == rhs.recovery_only && + lhs.verify == rhs.verify && lhs.no_emulated_sd == rhs.no_emulated_sd && lhs.no_trim == rhs.no_trim && lhs.file_encryption == rhs.file_encryption && @@ -211,6 +212,7 @@ bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) { lhs.slot_select == rhs.slot_select && lhs.late_mount == rhs.late_mount && lhs.no_fail == rhs.no_fail && + lhs.verify_at_boot == rhs.verify_at_boot && lhs.quota == rhs.quota && lhs.avb == rhs.avb && lhs.logical == rhs.logical && @@ -436,7 +438,7 @@ TEST(fs_mgr, ReadFstabFromFile_FsMgrFlags) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); std::string fstab_contents = R"fs( -source none0 swap defaults wait,check,nonremovable,recoveryonly +source none0 swap defaults wait,check,nonremovable,recoveryonly,verifyatboot,verify source none1 swap defaults avb,noemulatedsd,notrim,formattable,nofail source none2 swap defaults first_stage_mount,latemount,quota,logical source none3 swap defaults checkpoint=block @@ -457,6 +459,8 @@ source none5 swap defaults defaults flags.check = true; flags.nonremovable = true; flags.recovery_only = true; + flags.verify_at_boot = true; + flags.verify = true; EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags)); } diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index d0f68a80c26b..2117f72314a5 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -83,51 +83,28 @@ class FirstStageMountVBootV2 : public FirstStageMount { FirstStageMountVBootV2(Fstab fstab); virtual ~FirstStageMountVBootV2() = default; - bool DoCreateDevices() override; - bool DoFirstStageMount() override; + protected: + bool GetDmVerityDevices(std::set* devices) override; + bool SetUpDmVerity(FstabEntry* fstab_entry) override; private: - bool InitDevices(); - bool InitRequiredDevices(std::set devices); - bool CreateLogicalPartitions(); - bool CreateSnapshotPartitions(SnapshotManager* sm); - bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, - Fstab::iterator* end = nullptr); - - bool MountPartitions(); - bool TrySwitchSystemAsRoot(); - bool IsDmLinearEnabled(); - void GetSuperDeviceName(std::set* devices); - bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata); - void UseDsuIfPresent(); - // Reads all fstab.avb_keys from the ramdisk for first-stage mount. - void PreloadAvbKeys(); - // Copies /avb/*.avbpubkey used for DSU from the ramdisk to /metadata for key - // revocation check by DSU installation service. - void CopyDsuAvbKeys(); - - bool GetDmVerityDevices(std::set* devices); - bool SetUpDmVerity(FstabEntry* fstab_entry); bool InitAvbHandle(); - bool need_dm_verity_; - bool dsu_not_on_userdata_ = false; - bool use_snapuserd_ = false; - - Fstab fstab_; - // The super path is only set after InitDevices, and is invalid before. - std::string super_path_; - std::string super_partition_name_; - BlockDevInitializer block_dev_init_; - // Reads all AVB keys before chroot into /system, as they might be used - // later when mounting other partitions, e.g., /vendor and /product. - std::map> preload_avb_key_blobs_; - std::vector vbmeta_partitions_; AvbUniquePtr avb_handle_; }; +class FirstStageMountVBootV1 : public FirstStageMount { + public: + FirstStageMountVBootV1(Fstab fstab) : FirstStageMount(std::move(fstab)) {} + ~FirstStageMountVBootV1() override = default; + + protected: + bool GetDmVerityDevices(std::set* devices) override; + bool SetUpDmVerity(FstabEntry* fstab_entry) override; +}; + // Static Functions // ---------------- static inline bool IsDtVbmetaCompatible(const Fstab& fstab) { @@ -197,6 +174,8 @@ static bool GetRootEntry(FstabEntry* root_entry) { auto& dm = android::dm::DeviceMapper::Instance(); if (dm.GetState("vroot") != android::dm::DmDeviceState::INVALID) { root_entry->fs_mgr_flags.avb = true; + } else { + root_entry->fs_mgr_flags.verify = true; } return true; } @@ -224,6 +203,10 @@ static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta, return rollbacked; } +FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) { + super_partition_name_ = fs_mgr_get_super_partition_name(); +} + Result> FirstStageMount::Create(const std::string& cmdline) { Result fstab; if (IsMicrodroid()) { @@ -235,10 +218,14 @@ Result> FirstStageMount::Create(const std::stri return fstab.error(); } - return std::make_unique(std::move(*fstab)); + if (IsDtVbmetaCompatible(*fstab)) { + return std::make_unique(std::move(*fstab)); + } else { + return std::make_unique(std::move(*fstab)); + } } -bool FirstStageMountVBootV2::DoCreateDevices() { +bool FirstStageMount::DoCreateDevices() { if (!InitDevices()) return false; // Mount /metadata before creating logical partitions, since we need to @@ -260,7 +247,7 @@ bool FirstStageMountVBootV2::DoCreateDevices() { return true; } -bool FirstStageMountVBootV2::DoFirstStageMount() { +bool FirstStageMount::DoFirstStageMount() { if (!IsDmLinearEnabled() && fstab_.empty()) { // Nothing to mount. LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)"; @@ -272,7 +259,7 @@ bool FirstStageMountVBootV2::DoFirstStageMount() { return true; } -bool FirstStageMountVBootV2::InitDevices() { +bool FirstStageMount::InitDevices() { std::set devices; GetSuperDeviceName(&devices); @@ -293,14 +280,14 @@ bool FirstStageMountVBootV2::InitDevices() { return true; } -bool FirstStageMountVBootV2::IsDmLinearEnabled() { +bool FirstStageMount::IsDmLinearEnabled() { for (const auto& entry : fstab_) { if (entry.fs_mgr_flags.logical) return true; } return false; } -void FirstStageMountVBootV2::GetSuperDeviceName(std::set* devices) { +void FirstStageMount::GetSuperDeviceName(std::set* devices) { // Add any additional devices required for dm-linear mappings. if (!IsDmLinearEnabled()) { return; @@ -312,7 +299,7 @@ void FirstStageMountVBootV2::GetSuperDeviceName(std::set* devices) // Creates devices with uevent->partition_name matching ones in the given set. // Found partitions will then be removed from it for the subsequent member // function to check which devices are NOT created. -bool FirstStageMountVBootV2::InitRequiredDevices(std::set devices) { +bool FirstStageMount::InitRequiredDevices(std::set devices) { if (!block_dev_init_.InitDeviceMapper()) { return false; } @@ -322,7 +309,7 @@ bool FirstStageMountVBootV2::InitRequiredDevices(std::set devices) return block_dev_init_.InitDevices(std::move(devices)); } -bool FirstStageMountVBootV2::InitDmLinearBackingDevices( +bool FirstStageMount::InitDmLinearBackingDevices( const android::fs_mgr::LpMetadata& metadata) { std::set devices; @@ -340,7 +327,7 @@ bool FirstStageMountVBootV2::InitDmLinearBackingDevices( return InitRequiredDevices(std::move(devices)); } -bool FirstStageMountVBootV2::CreateLogicalPartitions() { +bool FirstStageMount::CreateLogicalPartitions() { if (!IsDmLinearEnabled()) { return true; } @@ -371,7 +358,7 @@ bool FirstStageMountVBootV2::CreateLogicalPartitions() { return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_); } -bool FirstStageMountVBootV2::CreateSnapshotPartitions(SnapshotManager* sm) { +bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) { // When COW images are present for snapshots, they are stored on // the data partition. if (!InitRequiredDevices({"userdata"})) { @@ -406,7 +393,7 @@ bool FirstStageMountVBootV2::CreateSnapshotPartitions(SnapshotManager* sm) { return true; } -bool FirstStageMountVBootV2::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, +bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, Fstab::iterator* end) { // Sets end to begin + 1, so we can just return on failure below. if (end) { @@ -451,7 +438,7 @@ bool FirstStageMountVBootV2::MountPartition(const Fstab::iterator& begin, bool e return mounted; } -void FirstStageMountVBootV2::PreloadAvbKeys() { +void FirstStageMount::PreloadAvbKeys() { for (const auto& entry : fstab_) { // No need to cache the key content if it's empty, or is already cached. if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) { @@ -498,7 +485,7 @@ void FirstStageMountVBootV2::PreloadAvbKeys() { // If system is in the fstab then we're not a system-as-root device, and in // this case, we mount system first then pivot to it. From that point on, // we are effectively identical to a system-as-root device. -bool FirstStageMountVBootV2::TrySwitchSystemAsRoot() { +bool FirstStageMount::TrySwitchSystemAsRoot() { UseDsuIfPresent(); // Preloading all AVB keys from the ramdisk before switching root to /system. PreloadAvbKeys(); @@ -527,7 +514,7 @@ bool FirstStageMountVBootV2::TrySwitchSystemAsRoot() { return true; } -bool FirstStageMountVBootV2::MountPartitions() { +bool FirstStageMount::MountPartitions() { if (!TrySwitchSystemAsRoot()) return false; if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false; @@ -610,7 +597,7 @@ bool FirstStageMountVBootV2::MountPartitions() { // copy files to /metadata is NOT fatal, because it is auxiliary to perform // public key matching before booting into DSU images on next boot. The actual // public key matching will still be done on next boot to DSU. -void FirstStageMountVBootV2::CopyDsuAvbKeys() { +void FirstStageMount::CopyDsuAvbKeys() { std::error_code ec; // Removing existing keys in gsi::kDsuAvbKeyDir as they might be stale. std::filesystem::remove_all(gsi::kDsuAvbKeyDir, ec); @@ -626,7 +613,7 @@ void FirstStageMountVBootV2::CopyDsuAvbKeys() { } } -void FirstStageMountVBootV2::UseDsuIfPresent() { +void FirstStageMount::UseDsuIfPresent() { std::string error; if (!android::gsi::CanBootIntoGsi(&error)) { @@ -663,9 +650,58 @@ void FirstStageMountVBootV2::UseDsuIfPresent() { TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions); } +bool FirstStageMountVBootV1::GetDmVerityDevices(std::set* devices) { + need_dm_verity_ = false; + + for (const auto& fstab_entry : fstab_) { + // Don't allow verifyatboot in the first stage. + if (fstab_entry.fs_mgr_flags.verify_at_boot) { + LOG(ERROR) << "Partitions can't be verified at boot"; + return false; + } + // Checks for verified partitions. + if (fstab_entry.fs_mgr_flags.verify) { + need_dm_verity_ = true; + } + } + + // Includes the partition names of fstab records. + // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used. + for (const auto& fstab_entry : fstab_) { + // Skip pseudo filesystems. + if (fstab_entry.fs_type == "overlay") { + continue; + } + if (!fstab_entry.fs_mgr_flags.logical) { + devices->emplace(basename(fstab_entry.blk_device.c_str())); + } + } + + return true; +} + +bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) { + if (fstab_entry->fs_mgr_flags.verify) { + int ret = fs_mgr_setup_verity(fstab_entry, false /* wait_for_verity_dev */); + switch (ret) { + case FS_MGR_SETUP_VERITY_SKIPPED: + case FS_MGR_SETUP_VERITY_DISABLED: + LOG(INFO) << "Verity disabled/skipped for '" << fstab_entry->mount_point << "'"; + return true; + case FS_MGR_SETUP_VERITY_SUCCESS: + // The exact block device name (fstab_rec->blk_device) is changed to + // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init + // first stage. + return block_dev_init_.InitDmDevice(fstab_entry->blk_device); + default: + return false; + } + } + return true; // Returns true to mount the partition. +} + FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab) - : need_dm_verity_(false), fstab_(std::move(fstab)), avb_handle_(nullptr) { - super_partition_name_ = fs_mgr_get_super_partition_name(); + : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) { std::string device_tree_vbmeta_parts; read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts); diff --git a/init/first_stage_mount.h b/init/first_stage_mount.h index 54501d8df6f5..47fd77344d5b 100644 --- a/init/first_stage_mount.h +++ b/init/first_stage_mount.h @@ -17,8 +17,13 @@ #pragma once #include +#include #include "result.h" +#include "fs_mgr.h" +#include "block_dev_initializer.h" + +#include namespace android { namespace init { @@ -30,12 +35,48 @@ class FirstStageMount { // The factory method to create a FirstStageMount instance. static Result> Create(const std::string& cmdline); // Creates devices and logical partitions from storage devices - virtual bool DoCreateDevices() = 0; + bool DoCreateDevices(); // Mounts fstab entries read from device tree. - virtual bool DoFirstStageMount() = 0; + bool DoFirstStageMount(); protected: - FirstStageMount() = default; + FirstStageMount(fs_mgr::Fstab fstab); + + bool InitDevices(); + bool InitRequiredDevices(std::set devices); + bool CreateLogicalPartitions(); + bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm); + bool MountPartition(const fs_mgr::Fstab::iterator& begin, bool erase_same_mounts, + fs_mgr::Fstab::iterator* end = nullptr); + + bool MountPartitions(); + bool TrySwitchSystemAsRoot(); + bool IsDmLinearEnabled(); + void GetSuperDeviceName(std::set* devices); + bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata); + void UseDsuIfPresent(); + // Reads all fstab.avb_keys from the ramdisk for first-stage mount. + void PreloadAvbKeys(); + // Copies /avb/*.avbpubkey used for DSU from the ramdisk to /metadata for key + // revocation check by DSU installation service. + void CopyDsuAvbKeys(); + + virtual bool GetDmVerityDevices(std::set* devices) = 0; + virtual bool SetUpDmVerity(fs_mgr::FstabEntry* fstab_entry) = 0; + + bool need_dm_verity_; + bool dsu_not_on_userdata_ = false; + bool use_snapuserd_ = false; + + fs_mgr::Fstab fstab_; + // The super path is only set after InitDevices, and is invalid before. + std::string super_path_; + std::string super_partition_name_; + BlockDevInitializer block_dev_init_; + // Reads all AVB keys before chroot into /system, as they might be used + // later when mounting other partitions, e.g., /vendor and /product. + std::map> preload_avb_key_blobs_; + }; void SetInitAvbVersionInRecovery();