Exemple #1
0
static retvalue checkpoolfile(const char *fullfilename, const struct checksums *expected, bool *improveable) {
	struct checksums *actual;
	retvalue r;
	bool improves;

	r = checksums_read(fullfilename, &actual);
	if (RET_IS_OK(r)) {
		if (!checksums_check(expected, actual, &improves)) {
			fprintf(stderr, "WRONG CHECKSUMS of '%s':\n",
					fullfilename);
			checksums_printdifferences(stderr, expected, actual);
			r = RET_ERROR_WRONG_MD5;
		} else if (improves)
			*improveable = true;
		checksums_free(actual);
	}
	return r;
}
Exemple #2
0
retvalue files_detect(const char *filekey) {
	struct checksums *checksums;
	char *fullfilename;
	retvalue r;

	fullfilename = files_calcfullfilename(filekey);
	if (FAILEDTOALLOC(fullfilename))
		return RET_ERROR_OOM;
	r = checksums_read(fullfilename, &checksums);
	if (r == RET_NOTHING) {
		fprintf(stderr, "Error opening '%s'!\n", fullfilename);
		r = RET_ERROR_MISSING;
	}
	if (RET_WAS_ERROR(r)) {
		free(fullfilename);
		return r;
	}
	free(fullfilename);
	r = files_add_checksums(filekey, checksums);
	checksums_free(checksums);
	return r;
}
/*!
 * \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;
}
Exemple #5
0
static retvalue checkimproveorinclude(const char *sourcedir, const char *basefilename, const char *filekey, struct checksums **checksums_p, bool *improving) {
	retvalue r;
	struct checksums *checksums = NULL;
	bool improves, copied = false;
	char *fullfilename = files_calcfullfilename(filekey);

	if (FAILEDTOALLOC(fullfilename))
		return RET_ERROR_OOM;

	if (checksums_iscomplete(*checksums_p)) {
		r = checksums_cheaptest(fullfilename, *checksums_p, true);
		if (r != RET_NOTHING) {
			free(fullfilename);
			return r;
		}
	} else {
		r = checksums_read(fullfilename, &checksums);
		if (RET_WAS_ERROR(r)) {
			free(fullfilename);
			return r;
		}
	}
	if (r == RET_NOTHING) {
		char *sourcefilename = calc_dirconcat(sourcedir, basefilename);

		if (FAILEDTOALLOC(sourcefilename)) {
			free(fullfilename);
			return RET_ERROR_OOM;
		}

		fprintf(stderr,
"WARNING: file %s was lost!\n"
"(i.e. found in the database, but not in the pool)\n"
"trying to compensate...\n",
				filekey);
		(void)dirs_make_parent(fullfilename);
		r = checksums_copyfile(fullfilename, sourcefilename, false,
				&checksums);
		if (r == RET_ERROR_EXIST) {
			fprintf(stderr,
"File '%s' seems to be missing and existing at the same time!\n"
"To confused to continue...\n",
					fullfilename);
		}
		if (r == RET_NOTHING) {
			fprintf(stderr, "Could not open '%s'!\n",
					sourcefilename);
			r = RET_ERROR_MISSING;
		}
		free(sourcefilename);
		if (RET_WAS_ERROR(r)) {
			free(fullfilename);
			return r;
		}
		copied = true;
	}

	assert (checksums != NULL);

	if (!checksums_check(*checksums_p, checksums, &improves)) {
		if (copied) {
			deletefile(fullfilename);
			fprintf(stderr,
"ERROR: Unexpected content of file '%s/%s'!\n", sourcedir, basefilename);
		} else
// TODO: if the database only listed some of the currently supported checksums,
// and the caller of checkincludefile supplied some (which none yet does), but
// not all (which needs at least three checksums, i.e. not applicaple before
// sha256 get added), then this might also be called if the file in the pool
// just has the same checksums as previously recorded (e.g. a md5sum collision)
// but the new file was listed with another secondary hash than the original.
// In that situation it might be a bit misleading...
			fprintf(stderr,
"ERROR: file %s is damaged!\n"
"(i.e. found in the database, but with different checksums in the pool)\n",
				filekey);
		checksums_printdifferences(stderr, *checksums_p, checksums);
		r = RET_ERROR_WRONG_MD5;
	}
	if (improves) {
		r = checksums_combine(checksums_p, checksums, NULL);
		if (RET_IS_OK(r))
			*improving = true;
	}
	checksums_free(checksums);
	free(fullfilename);
	return r;
}
Exemple #6
0
/* Include a yet unknown file into the pool */
retvalue files_preinclude(const char *sourcefilename, const char *filekey, struct checksums **checksums_p) {
	retvalue r;
	struct checksums *checksums, *realchecksums;
	bool improves;
	char *fullfilename;

	r = files_get_checksums(filekey, &checksums);
	if (RET_WAS_ERROR(r))
		return r;
	if (RET_IS_OK(r)) {
		r = checksums_read(sourcefilename, &realchecksums);
		if (r == RET_NOTHING)
			r = RET_ERROR_MISSING;
		if (RET_WAS_ERROR(r)) {
			checksums_free(checksums);
			return r;
		}
		if (!checksums_check(checksums, realchecksums, &improves)) {
			fprintf(stderr,
"ERROR: '%s' cannot be included as '%s'.\n"
"Already existing files can only be included again, if they are the same, but:\n",
				sourcefilename, filekey);
			checksums_printdifferences(stderr, checksums,
					realchecksums);
			checksums_free(checksums);
			checksums_free(realchecksums);
			return RET_ERROR_WRONG_MD5;
		}
		if (improves) {
			r = checksums_combine(&checksums, realchecksums, NULL);
			if (RET_WAS_ERROR(r)) {
				checksums_free(realchecksums);
				checksums_free(checksums);
				return r;
			}
			r = files_replace_checksums(filekey, checksums);
			if (RET_WAS_ERROR(r)) {
				checksums_free(realchecksums);
				checksums_free(checksums);
				return r;
			}
		}
		checksums_free(realchecksums);
		// args, this breaks retvalue semantics!
		if (checksums_p != NULL)
			*checksums_p = checksums;
		else
			checksums_free(checksums);
		return RET_NOTHING;
	}
	assert (sourcefilename != NULL);
	fullfilename = files_calcfullfilename(filekey);
	if (FAILEDTOALLOC(fullfilename))
		return RET_ERROR_OOM;
	(void)dirs_make_parent(fullfilename);
	r = checksums_copyfile(fullfilename, sourcefilename, true, &checksums);
	if (r == RET_ERROR_EXIST) {
		// TODO: deal with already existing files!
		fprintf(stderr, "File '%s' does already exist!\n",
				fullfilename);
	}
	if (r == RET_NOTHING) {
		fprintf(stderr, "Could not open '%s'!\n", sourcefilename);
		r = RET_ERROR_MISSING;
	}
	if (RET_WAS_ERROR(r)) {
		free(fullfilename);
		return r;
	}
	free(fullfilename);

	r = files_add_checksums(filekey, checksums);
	if (RET_WAS_ERROR(r)) {
		checksums_free(checksums);
		return r;
	}
	if (checksums_p != NULL)
		*checksums_p = checksums;
	else
		checksums_free(checksums);
	return RET_OK;
}