Exemple #1
0
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;
}
bool mount_fstab(const std::string &fstab_path)
{
    bool ret = true;

    std::vector<util::fstab_rec> fstab;
    std::vector<util::fstab_rec *> recs_system;
    std::vector<util::fstab_rec *> recs_cache;
    std::vector<util::fstab_rec *> recs_data;
    std::vector<util::fstab_rec *> flags_system;
    std::vector<util::fstab_rec *> flags_cache;
    std::vector<util::fstab_rec *> flags_data;
    std::string target_system;
    std::string target_cache;
    std::string target_data;
    std::string path_fstab_gen;
    std::string path_completed;
    std::string path_failed;
    std::string base_name;
    std::string dir_name;
    struct stat st;
    std::shared_ptr<Rom> rom;
    std::string rom_id;

    Roms roms;
    roms.add_builtin();

    base_name = util::base_name(fstab_path);
    dir_name = util::dir_name(fstab_path);

    path_fstab_gen += dir_name;
    path_fstab_gen += "/.";
    path_fstab_gen += base_name;
    path_fstab_gen += ".gen";
    path_completed += dir_name;
    path_completed += "/.";
    path_completed += base_name;
    path_completed += ".completed";
    path_failed += dir_name;
    path_failed += "/.";
    path_failed += base_name;
    path_failed += ".failed";

    auto on_finish = util::finally([&] {
        if (ret) {
            util::create_empty_file(path_completed);
            LOGI("Successfully mounted partitions");
        } else {
            util::create_empty_file(path_failed);
        }
    });

    // This is a oneshot operation
    if (stat(path_completed.c_str(), &st) == 0) {
        LOGV("Filesystems already successfully mounted");
        return true;
    }

    if (stat(path_failed.c_str(), &st) == 0) {
        LOGE("Failed to mount partitions ealier. No further attempts will be made");
        return false;
    }

    // Remount rootfs as read-write so a new fstab file can be written
    if (mount("", "/", "", MS_REMOUNT, "") < 0) {
        LOGE("Failed to remount rootfs as rw: {}", strerror(errno));
    }

    // Read original fstab
    fstab = util::read_fstab(fstab_path);
    if (fstab.empty()) {
        LOGE("Failed to read {}", fstab_path);
        return false;
    }

    // Generate new fstab without /system, /cache, or /data entries
    file_ptr out(std::fopen(path_fstab_gen.c_str(), "wb"), std::fclose);
    if (!out) {
        LOGE("Failed to open {} for writing: {}",
             path_fstab_gen, strerror(errno));
        return false;
    }

    for (util::fstab_rec &rec : fstab) {
        if (rec.mount_point == "/system") {
            recs_system.push_back(&rec);
        } else if (rec.mount_point == "/cache") {
            recs_cache.push_back(&rec);
        } else if (rec.mount_point == "/data") {
            recs_data.push_back(&rec);
        } else {
            std::fprintf(out.get(), "%s\n", rec.orig_line.c_str());
        }
    }

    out.reset();

    // /system and /data are always in the fstab. The patcher should create
    // an entry for /cache for the ROMs that mount it manually in one of the
    // init scripts
    if (recs_system.empty() || recs_cache.empty() || recs_data.empty()) {
        LOGE("fstab does not contain all of /system, /cache, and /data!");
        return false;
    }

    // Mount raw partitions to /raw/*
    if (!util::kernel_cmdline_get_option("romid", &rom_id)
            && !util::file_first_line("/romid", &rom_id)) {
        LOGE("Failed to determine ROM ID");
        return false;
    }

    if (Roms::is_named_rom(rom_id)) {
        rom = Roms::create_named_rom(rom_id);
    } else {
        rom = roms.find_by_id(rom_id);
        if (!rom) {
            LOGE("Unknown ROM ID: {}", rom_id);
            return false;
        }
    }

    LOGD("ROM ID is: {}", rom_id);

    // Set property for the Android app to use
    if (!util::set_property("ro.multiboot.romid", rom_id)) {
        LOGE("Failed to set 'ro.multiboot.romid' to '{}'", rom_id);
    }

    // Because of how Android deals with partitions, if, say, the source path
    // for the /system bind mount resides on /cache, then the cache partition
    // must be mounted with the system partition's flags. In this future, this
    // may be avoided by mounting every partition with some more liberal flags,
    // since the current setup does not allow two bind mounted locations to
    // reside on the same partition.

    if (util::starts_with(rom->system_path, "/cache")) {
        flags_system = recs_cache;
    } else {
        flags_system = recs_system;
    }

    if (util::starts_with(rom->cache_path, "/system")) {
        flags_cache = recs_system;
    } else {
        flags_cache = recs_cache;
    }

    flags_data = recs_data;

    if (mkdir("/raw", 0755) < 0) {
        LOGE("Failed to create /raw");
        return false;
    }

    if (!create_dir_and_mount(recs_system, flags_system, "/raw/system")) {
        LOGE("Failed to mount /raw/system");
        return false;
    }
    if (!create_dir_and_mount(recs_cache, flags_cache, "/raw/cache")) {
        LOGE("Failed to mount /raw/cache");
        return false;
    }
    if (!create_dir_and_mount(recs_data, flags_data, "/raw/data")) {
        LOGE("Failed to mount /raw/data");
        return false;
    }

    // Make paths use /raw/...
    if (rom->system_path.empty()
            || rom->cache_path.empty()
            || rom->data_path.empty()) {
        LOGE("Invalid or empty paths");
        return false;
    }

    target_system += "/raw";
    target_system += rom->system_path;
    target_cache += "/raw";
    target_cache += rom->cache_path;
    target_data += "/raw";
    target_data += rom->data_path;

    if (!util::bind_mount(target_system, 0771, "/system", 0771)) {
        return false;
    }

    if (!util::bind_mount(target_cache, 0771, "/cache", 0771)) {
        return false;
    }

    if (!util::bind_mount(target_data, 0771, "/data", 0771)) {
        return false;
    }

    // Bind mount internal SD directory
    if (!util::bind_mount("/raw/data/media", 0771, "/data/media", 0771)) {
        return false;
    }

    // Prevent installd from dying because it can't unmount /data/media for
    // multi-user migration. Since <= 4.2 devices aren't supported anyway,
    // we'll bypass this.
    file_ptr fp(std::fopen("/data/.layout_version", "wb"), std::fclose);
    if (fp) {
        const char *layout_version;
        if (get_api_version() >= 21) {
            layout_version = "3";
        } else {
            layout_version = "2";
        }

        fwrite(layout_version, 1, strlen(layout_version), fp.get());
        fp.reset();
    } else {
        LOGE("Failed to open /data/.layout_version to disable migration");
    }

    static std::string context("u:object_r:install_data_file:s0");
    if (lsetxattr("/data/.layout_version", "security.selinux",
                  context.c_str(), context.size() + 1, 0) < 0) {
        LOGE("{}: Failed to set SELinux context: {}",
             "/data/.layout_version", strerror(errno));
    }


    // Global app sharing
    std::string config_path("/data/media/0/MultiBoot/");
    config_path += rom->id;
    config_path += "/config.json";

    RomConfig config;
    if (config.load_file(config_path)) {
        if (config.indiv_app_sharing && (config.global_app_sharing
                || config.global_paid_app_sharing)) {
            LOGW("Both individual and global sharing are enabled");
            LOGW("Global sharing settings will be ignored");
        } else {
            if (config.global_app_sharing || config.global_paid_app_sharing) {
                if (!util::bind_mount("/raw/data/app-lib", 0771,
                                      "/data/app-lib", 0771)) {
                    return false;
                }
            }
            if (config.global_app_sharing) {
                if (!util::bind_mount("/raw/data/app", 0771,
                                      "/data/app", 0771)) {
                    return false;
                }
            }
            if (config.global_paid_app_sharing) {
                if (!util::bind_mount("/raw/data/app-asec", 0771,
                                      "/data/app-asec", 0771)) {
                    return false;
                }
            }
        }
    }

    return true;
}
Exemple #5
0
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>();
}