Exemplo n.º 1
0
/*!
 * \brief Write checksums properties to \a /data/multiboot/checksums.prop
 *
 * \param props Properties map
 *
 * \return True if successfully written. Otherwise, false.
 */
bool checksums_write(const std::unordered_map<std::string, std::string> &props)
{
    std::string checksums_path = get_raw_path(CHECKSUMS_PATH);

    if (remove(checksums_path.c_str()) < 0 && errno != ENOENT) {
        LOGW("%s: Failed to remove file: %s",
             checksums_path.c_str(), strerror(errno));
    }

    util::mkdir_parent(checksums_path, 0755);
    util::create_empty_file(checksums_path);

    if (!util::chown(checksums_path, 0, 0, 0)) {
        LOGW("%s: Failed to chown file: %s",
             checksums_path.c_str(), strerror(errno));
    }
    if (chmod(checksums_path.c_str(), 0700) < 0) {
        LOGW("%s: Failed to chmod file: %s",
             checksums_path.c_str(), strerror(errno));
    }

    if (!util::file_write_properties(checksums_path, props)) {
        LOGW("%s: Failed to write new properties: %s",
             checksums_path.c_str(), strerror(errno));
        return false;
    }

    return true;
}
Exemplo n.º 2
0
/*!
 * \brief Check a checksum property
 *
 * \param props Pointer to properties map
 * \param rom_id ROM ID
 * \param image Image filename (without directory)
 * \param sha512_out SHA512 hex digest output
 *
 * \return ChecksumsGetResult::FOUND if the hash was successfully retrieved,
 *         ChecksumsGetResult::NOT_FOUND if the hash does not exist in the map,
 *         ChecksumsGetResult::MALFORMED if the property has an invalid format
 */
ChecksumsGetResult checksums_get(std::unordered_map<std::string, std::string> *props,
                                 const std::string &rom_id,
                                 const std::string &image,
                                 std::string *sha512_out)
{
    std::string checksums_path = get_raw_path(CHECKSUMS_PATH);

    std::string key(rom_id);
    key += "/";
    key += image;

    if (props->find(key) == props->end()) {
        return ChecksumsGetResult::NOT_FOUND;
    }

    const std::string &value = (*props)[key];

    std::size_t pos = value.find(":");
    if (pos != std::string::npos) {
        std::string algo = value.substr(0, pos);
        std::string hash = value.substr(pos + 1);
        if (algo != "sha512") {
            LOGE("%s: Invalid hash algorithm: %s",
                 checksums_path.c_str(), algo.c_str());
            return ChecksumsGetResult::MALFORMED;
        }

        *sha512_out = hash;
        return ChecksumsGetResult::FOUND;
    } else {
        LOGE("%s: Invalid checksum property: %s=%s",
             checksums_path.c_str(), key.c_str(), value.c_str());
        return ChecksumsGetResult::MALFORMED;
    }
}
Exemplo n.º 3
0
void Roms::add_data_roms()
{
    std::string system = get_raw_path("/data/multiboot");

    DIR *dp = opendir(system.c_str());
    if (!dp ) {
        return;
    }

    auto close_dp = util::finally([&]{
        closedir(dp);
    });

    struct stat sb;

    struct dirent *ent;
    while ((ent = readdir(dp))) {
        if (strcmp(ent->d_name, "data-slot-") == 0
                || !util::starts_with(ent->d_name, "data-slot-")) {
            continue;
        }

        std::string fullpath(system);
        fullpath += "/";
        fullpath += ent->d_name;

        if (stat(fullpath.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
            roms.push_back(create_rom_data_slot(ent->d_name + 10));
        }
    }
}
Exemplo n.º 4
0
/*!
 * \brief Read checksums properties from \a /data/multiboot/checksums.prop
 *
 * \param props Pointer to properties map
 *
 * \return True if the file was successfully read. Otherwise, false.
 */
bool checksums_read(std::unordered_map<std::string, std::string> *props)
{
    std::string checksums_path = get_raw_path(CHECKSUMS_PATH);

    if (!util::file_get_all_properties(checksums_path, props)) {
        LOGE("%s: Failed to load properties", checksums_path.c_str());
        return false;
    }
    return true;
}
Exemplo n.º 5
0
static bool daemon_init()
{
    if (chdir("/") < 0) {
        LOGE("Failed to change cwd to /: %s", strerror(errno));
        return false;
    }

    umask(0);

    if (!log_to_stdio && !redirect_stdio_to_dev_null()) {
        return false;
    }

    // Set up logging
    if (log_to_stdio) {
        // Default; do nothing
    } else if (log_to_kmsg) {
        log::log_set_logger(std::make_shared<log::KmsgLogger>(false));
    } else {
        if (!util::mkdir_parent(MULTIBOOT_LOG_DAEMON, 0775)
                && errno != EEXIST) {
            LOGE("Failed to create parent directory of %s: %s",
                 MULTIBOOT_LOG_DAEMON, strerror(errno));
            return false;
        }

        log_fp = autoclose::fopen(
                get_raw_path(MULTIBOOT_LOG_DAEMON).c_str(), "w");
        if (!log_fp) {
            LOGE("Failed to open log file %s: %s",
                 MULTIBOOT_LOG_DAEMON, strerror(errno));
            return false;
        }

        fix_multiboot_permissions();

        // mbtool logging
        log::log_set_logger(
                std::make_shared<log::StdioLogger>(log_fp.get(), true));
    }

    LOGD("Initialized daemon");

    return true;
}
Exemplo n.º 6
0
void Roms::add_data_roms()
{
    std::string system = get_raw_path("/data/multiboot");

    DIR *dp = opendir(system.c_str());
    if (!dp ) {
        return;
    }

    auto close_dp = util::finally([&]{
        closedir(dp);
    });

    struct stat sb;

    std::vector<std::shared_ptr<Rom>> temp_roms;

    struct dirent *ent;
    while ((ent = readdir(dp))) {
        if (strcmp(ent->d_name, "data-slot-") == 0
                || !util::starts_with(ent->d_name, "data-slot-")) {
            continue;
        }

        std::string fullpath(system);
        fullpath += "/";
        fullpath += ent->d_name;

        if (stat(fullpath.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
            temp_roms.push_back(create_rom_data_slot(ent->d_name + 10));
        }
    }

    // Sort by ID
    std::sort(temp_roms.begin(), temp_roms.end(), &cmp_rom_id);
    std::move(temp_roms.begin(), temp_roms.end(), std::back_inserter(roms));
}
Exemplo n.º 7
0
/*!
 * \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;
}
Exemplo n.º 8
0
/*!
 * \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;
}
Exemplo n.º 9
0
std::string Rom::thumbnail_path()
{
    return get_raw_path(util::format(
            MULTIBOOT_DIR "/%s/thumbnail.webp", id.c_str()));
}
Exemplo n.º 10
0
std::string Rom::config_path()
{
    return get_raw_path(util::format(
            MULTIBOOT_DIR "/%s/config.json", id.c_str()));
}
Exemplo n.º 11
0
std::string Rom::boot_image_path()
{
    return get_raw_path(util::format(
            MULTIBOOT_DIR "/%s/boot.img", id.c_str()));
}