blob: 45780cafb21897ca51f5ad6fa8910b7f493f3eb5 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resources_integrity.h"
#include <algorithm>
#include <array>
#include "base/files/file.h"
#include "base/functional/bind.h"
#include "base/memory/page_size.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "chrome/common/chrome_paths.h"
#include "crypto/hash.h"
#include "crypto/secure_util.h"
#include "ui/base/buildflags.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "chrome/app/chrome_exe_main_win.h"
#else
#include "chrome/app/packed_resources_integrity.h" // nogncheck
#endif
namespace {
bool CheckResourceIntegrityInternal(
const base::FilePath& path,
base::span<const uint8_t, crypto::hash::kSha256Size> expected_signature) {
// Open the file for reading; allowing other consumers to also open it for
// reading and deleting. Do not allow others to write to it.
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WIN_EXCLUSIVE_WRITE |
base::File::FLAG_WIN_SHARE_DELETE);
if (!file.IsValid())
return false;
crypto::hash::Hasher hasher(crypto::hash::HashKind::kSha256);
std::vector<uint8_t> buffer(base::GetPageSize());
std::optional<size_t> bytes_read = 0;
do {
bytes_read = file.ReadAtCurrentPos(buffer);
if (!bytes_read.has_value()) {
return false;
}
hasher.Update(base::span(buffer).first(*bytes_read));
} while (bytes_read.value_or(0) > 0);
std::array<uint8_t, crypto::hash::kSha256Size> digest;
hasher.Finish(digest);
return crypto::SecureMemEqual(digest, expected_signature);
}
void ReportPakIntegrity(const std::string& histogram_name, bool hash_matches) {
base::UmaHistogramBoolean(histogram_name, hash_matches);
}
} // namespace
void CheckResourceIntegrity(
const base::FilePath& path,
base::span<const uint8_t, crypto::kSHA256Length> expected_signature,
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::OnceCallback<void(bool)> callback) {
task_runner->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&CheckResourceIntegrityInternal, path, expected_signature),
std::move(callback));
}
void CheckPakFileIntegrity() {
base::FilePath resources_pack_path;
base::PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
// On Windows, the hashes cannot be embedded in the chrome.dll target that
// this file is a part of, because it creates a cyclic build dependency
// with the Grit resource allow-list generation. Instead, the hashes are
// embedded in chrome.exe, which provides an exported function to
// access them.
#if BUILDFLAG(IS_WIN)
auto get_pak_file_hashes = reinterpret_cast<decltype(&GetPakFileHashes)>(
::GetProcAddress(::GetModuleHandle(nullptr), "GetPakFileHashes"));
if (!get_pak_file_hashes) {
// This is only exported by chrome.exe and unit_tests.exe, so in
// other tests, like browser_tests.exe, this export will not be available.
return;
}
const uint8_t *resources_hash_raw = nullptr, *chrome_100_hash_raw = nullptr,
*chrome_200_hash_raw = nullptr;
get_pak_file_hashes(&resources_hash_raw, &chrome_100_hash_raw,
&chrome_200_hash_raw);
UNSAFE_BUFFERS(
// SAFETY: these are compile-time constant data exposed via a C ABI
// function, which means they can't be directly returned as spans or
// std::arrays.
base::span resources_hash(
resources_hash_raw, base::fixed_extent<crypto::hash::kSha256Size>());
base::span chrome_100_hash(
chrome_100_hash_raw, base::fixed_extent<crypto::hash::kSha256Size>());
base::span chrome_200_hash(
chrome_200_hash_raw,
base::fixed_extent<crypto::hash::kSha256Size>());)
#else
base::span resources_hash = kSha256_resources_pak;
base::span chrome_100_hash = kSha256_chrome_100_percent_pak;
#if BUILDFLAG(ENABLE_HIDPI)
base::span chrome_200_hash = kSha256_chrome_200_percent_pak;
#endif
#endif // BUILDFLAG(IS_WIN)
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
CheckResourceIntegrity(resources_pack_path, resources_hash, task_runner,
base::BindOnce(&ReportPakIntegrity,
"SafeBrowsing.PakIntegrity.Resources"));
CheckResourceIntegrity(
resources_pack_path.DirName().AppendASCII("chrome_100_percent.pak"),
chrome_100_hash, task_runner,
base::BindOnce(&ReportPakIntegrity,
"SafeBrowsing.PakIntegrity.Chrome100"));
#if BUILDFLAG(ENABLE_HIDPI)
CheckResourceIntegrity(
resources_pack_path.DirName().AppendASCII("chrome_200_percent.pak"),
chrome_200_hash, task_runner,
base::BindOnce(&ReportPakIntegrity,
"SafeBrowsing.PakIntegrity.Chrome200"));
#endif
}