std::shared_ptr<Rom> Roms::get_current_rom() { Roms roms; roms.add_installed(); // This is set if mbtool is handling the boot process std::string prop_id; util::get_property("ro.multiboot.romid", &prop_id, std::string()); if (!prop_id.empty()) { auto rom = roms.find_by_id(prop_id); if (rom) { return rom; } } // If /raw/ or /raw-system/ does not exist, then this is an unpatched // primary ROM struct stat sb; bool has_raw = stat("/raw", &sb) == 0; bool has_raw_system = stat("/raw-system", &sb) == 0; if (!has_raw && !has_raw_system) { // Cache the result util::set_property("ro.multiboot.romid", "primary"); return roms.find_by_id("primary"); } // Otherwise, iterate through the installed ROMs if (stat("/system/build.prop", &sb) == 0) { for (auto rom : roms.roms) { // We can't check roms that use images since they aren't mounted if (rom->system_is_image) { continue; } std::string path = rom->full_system_path(); if (path.empty()) { continue; } path += "/build.prop"; struct stat sb2; if (stat(path.c_str(), &sb2) == 0 && sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) { // Cache the result util::set_property("ro.multiboot.romid", rom->id); return rom; } } } return std::shared_ptr<Rom>(); }
/*! * \brief Set the kernel for a ROM * * \note This will update the checksum for the image in * \a /data/multiboot/checksums.prop. * * \param id ROM ID to set the kernel for * \param boot_blockdev Block device path of the boot partition * * \return True if the kernel was successfully set. Otherwise, false. */ bool set_kernel(const char *id, const char *boot_blockdev) { LOGD("Attempting to set the kernel for %s", id); // Path for all of the images std::string multiboot_path(get_raw_path(MULTIBOOT_DIR)); multiboot_path += "/"; multiboot_path += id; std::string bootimg_path(multiboot_path); bootimg_path += "/boot.img"; // Verify ROM ID Roms roms; roms.add_installed(); auto r = roms.find_by_id(id); if (!r) { LOGE("Invalid ROM ID: %s", id); return false; } if (!util::mkdir_recursive(multiboot_path, 0775)) { LOGE("%s: Failed to create directory: %s", multiboot_path.c_str(), strerror(errno)); return false; } unsigned char *data; std::size_t size; if (!util::file_read_all(boot_blockdev, &data, &size)) { LOGE("%s: Failed to read block device: %s", boot_blockdev, strerror(errno)); return false; } auto free_data = util::finally([&]{ free(data); }); // Get actual sha512sum unsigned char digest[SHA512_DIGEST_LENGTH]; SHA512(data, size, digest); std::string hash = util::hex_string(digest, SHA512_DIGEST_LENGTH); // Add to checksums.prop std::unordered_map<std::string, std::string> props; checksums_read(&props); checksums_update(&props, id, "boot.img", hash); // NOTE: This function isn't responsible for updating the checksums for // any extra images. We don't want to mask any malicious changes. // Cast is okay. The data is just passed to fwrite (ie. no signed // extension issues) if (!util::file_write_data(bootimg_path, (char *) data, size)) { LOGE("%s: Failed to write image: %s", bootimg_path.c_str(), strerror(errno)); return false; } LOGD("Updating checksums file"); checksums_write(props); if (!fix_multiboot_permissions()) { //return false; } return true; }
/*! * \brief Switch to another ROM * * \note If the checksum is missing for some images to be flashed and invalid * for some other images to be flashed, this function will always return * SwitchRomResult::CHECKSUM_INVALID. * * \param id ROM ID to switch to * \param boot_blockdev Block device path of the boot partition * \param blockdev_base_dirs Search paths (non-recursive) for block devices * corresponding to extra flashable images in * /sdcard/MultiBoot/[ROM ID]/ *.img * * \return SwitchRomResult::SUCCEEDED if the switching succeeded, * SwitchRomResult::FAILED if the switching failed, * SwitchRomResult::CHECKSUM_NOT_FOUND if the checksum for some image is missing, * SwitchRomResult::CHECKSUM_INVALID if the checksum for some image is invalid * */ SwitchRomResult switch_rom(const char *id, const char *boot_blockdev, const char * const *blockdev_base_dirs, bool force_update_checksums) { LOGD("Attempting to switch to %s", id); LOGD("Force update checksums: %d", force_update_checksums); // Path for all of the images std::string multiboot_path(get_raw_path(MULTIBOOT_DIR)); multiboot_path += "/"; multiboot_path += id; std::string bootimg_path(multiboot_path); bootimg_path += "/boot.img"; // Verify ROM ID Roms roms; roms.add_installed(); auto r = roms.find_by_id(id); if (!r) { LOGE("Invalid ROM ID: %s", id); return SwitchRomResult::FAILED; } if (!util::mkdir_recursive(multiboot_path, 0775)) { LOGE("%s: Failed to create directory: %s", multiboot_path.c_str(), strerror(errno)); return SwitchRomResult::FAILED; } // We'll read the files we want to flash into memory so a malicious app // can't change the file between the hash verification step and flashing // step. std::vector<Flashable> flashables; auto free_flashables = util::finally([&]{ for (Flashable &f : flashables) { free(f.data); } }); flashables.emplace_back(); flashables.back().image = bootimg_path; flashables.back().block_dev = boot_blockdev; if (!add_extra_images(multiboot_path.c_str(), blockdev_base_dirs, &flashables)) { LOGW("Failed to find extra images"); } std::unordered_map<std::string, std::string> props; checksums_read(&props); for (Flashable &f : flashables) { // If memory becomes an issue, an alternative method is to create a // temporary directory in /data/multiboot/ that's only writable by root // and copy the images there. if (!util::file_read_all(f.image, &f.data, &f.size)) { LOGE("%s: Failed to read image: %s", f.image.c_str(), strerror(errno)); return SwitchRomResult::FAILED; } // Get actual sha512sum unsigned char digest[SHA512_DIGEST_LENGTH]; SHA512(f.data, f.size, digest); f.hash = util::hex_string(digest, SHA512_DIGEST_LENGTH); if (force_update_checksums) { checksums_update(&props, id, util::base_name(f.image), f.hash); } // Get expected sha512sum ChecksumsGetResult ret = checksums_get( &props, id, util::base_name(f.image), &f.expected_hash); if (ret == ChecksumsGetResult::MALFORMED) { return SwitchRomResult::CHECKSUM_INVALID; } // Verify hashes if we have an expected hash if (ret == ChecksumsGetResult::FOUND && f.expected_hash != f.hash) { LOGE("%s: Checksum (%s) does not match expected (%s)", f.image.c_str(), f.hash.c_str(), f.expected_hash.c_str()); return SwitchRomResult::CHECKSUM_INVALID; } } // Fail if we're missing expected hashes. We do this last to make sure // CHECKSUM_INVALID is returned if some checksums don't match (for the ones // that aren't missing). for (Flashable &f : flashables) { if (f.expected_hash.empty()) { LOGE("%s: Checksum does not exist", f.image.c_str()); return SwitchRomResult::CHECKSUM_NOT_FOUND; } } // Now we can flash the images for (Flashable &f : flashables) { // Cast is okay. The data is just passed to fwrite (ie. no signed // extension issues) if (!util::file_write_data(f.block_dev, (char *) f.data, f.size)) { LOGE("%s: Failed to write image: %s", f.block_dev.c_str(), strerror(errno)); return SwitchRomResult::FAILED; } } if (force_update_checksums) { LOGD("Updating checksums file"); checksums_write(props); } if (!fix_multiboot_permissions()) { //return SwitchRomResult::FAILED; } return SwitchRomResult::SUCCEEDED; }
std::shared_ptr<Rom> Roms::get_current_rom() { Roms roms; roms.add_installed(); // This is set if mbtool is handling the boot process char prop_id[PROP_VALUE_MAX]; util::property_get(PROP_MULTIBOOT_ROM_ID, prop_id, ""); // This is necessary for the daemon to get a correct result before Android // boots (eg. for the boot UI) if (!prop_id[0]) { std::string temp; util::file_get_property(DEFAULT_PROP_PATH, PROP_MULTIBOOT_ROM_ID, &temp, std::string()); strlcpy(prop_id, temp.c_str(), sizeof(prop_id)); } if (prop_id[0]) { auto rom = roms.find_by_id(prop_id); if (rom) { return rom; } } // If /raw/ or /raw-system/ does not exist, then this is an unpatched // primary ROM struct stat sb; bool has_raw = stat("/raw", &sb) == 0; bool has_raw_system = stat("/raw-system", &sb) == 0; if (!has_raw && !has_raw_system) { // Cache the result util::property_set(PROP_MULTIBOOT_ROM_ID, "primary"); return roms.find_by_id("primary"); } // Otherwise, iterate through the installed ROMs if (stat("/system/build.prop", &sb) == 0) { for (auto rom : roms.roms) { // We can't check roms that use images since they aren't mounted if (rom->system_is_image) { continue; } std::string path = rom->full_system_path(); if (path.empty()) { continue; } path += "/build.prop"; struct stat sb2; if (stat(path.c_str(), &sb2) == 0 && sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) { // Cache the result util::property_set(PROP_MULTIBOOT_ROM_ID, rom->id.c_str()); return rom; } } } return std::shared_ptr<Rom>(); }