blob: 7451e1ff45677362b075702b114bde6e06d9e1ee [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/component_updater/first_party_sets_component_installer.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/cxx17_backports.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
#include "components/component_updater/component_installer.h"
#include "components/component_updater/component_updater_paths.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/network_service_instance.h"
#include "net/base/features.h"
#include "net/cookies/cookie_util.h"
#include "services/network/public/mojom/network_service.mojom.h"
using component_updater::ComponentUpdateService;
namespace {
using SetsReadyOnceCallback = component_updater::
FirstPartySetsComponentInstallerPolicy::SetsReadyOnceCallback;
constexpr base::FilePath::CharType kFirstPartySetsSetsFileName[] =
FILE_PATH_LITERAL("sets.json");
// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
// The extension id is: gonpemdgkjcecdgbnaabipppbmgfggbe
constexpr uint8_t kFirstPartySetsPublicKeySHA256[32] = {
0x6e, 0xdf, 0x4c, 0x36, 0xa9, 0x24, 0x23, 0x61, 0xd0, 0x01, 0x8f,
0xff, 0x1c, 0x65, 0x66, 0x14, 0xa8, 0x46, 0x37, 0xe6, 0xeb, 0x80,
0x8b, 0x8f, 0xb0, 0xb6, 0x18, 0xa7, 0xcd, 0x3d, 0xbb, 0xfb};
constexpr char kFirstPartySetsManifestName[] = "First-Party Sets";
constexpr base::FilePath::CharType kFirstPartySetsRelativeInstallDir[] =
FILE_PATH_LITERAL("FirstPartySetsPreloaded");
base::File OpenFile(const base::FilePath& pb_path) {
return base::File(pb_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
}
base::FilePath& GetConfigPathInstance() {
static base::NoDestructor<base::FilePath> instance;
return *instance;
}
// Determine if First-Party Sets is enabled by checking both the feature
// and the enterprise policy.
bool IsFirstPartySetsEnabled() {
// TODO(https://crbug.com/1269360): move this logic into
// FirstPartySetsUtil
if (!base::FeatureList::IsEnabled(net::features::kFirstPartySets)) {
return false;
}
// Some java tests start the browser in minimal mode, in which
// `g_browser_process` is unset.
if (!g_browser_process)
return false;
auto* local_state = g_browser_process->local_state();
if (!local_state ||
!local_state->HasPrefPath(first_party_sets::kFirstPartySetsEnabled)) {
return true;
}
return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
}
// Invokes `on_sets_ready`, if:
// * First-Party Sets is enabled; and
// * `on_sets_ready` is not null.
//
// If the component has been installed and can be read, we pass the component
// file; otherwise, we pass an invalid file.
void SetFirstPartySetsConfig(SetsReadyOnceCallback on_sets_ready) {
if (!IsFirstPartySetsEnabled() || on_sets_ready.is_null()) {
return;
}
const base::FilePath instance_path = GetConfigPathInstance();
if (instance_path.empty()) {
// Registration is complete, but no component version exists on disk.
std::move(on_sets_ready).Run(base::File());
return;
}
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&OpenFile, instance_path), std::move(on_sets_ready));
}
std::string BoolToString(bool b) {
return b ? "true" : "false";
}
} // namespace
namespace component_updater {
// static
void FirstPartySetsComponentInstallerPolicy::ReconfigureAfterNetworkRestart(
SetsReadyOnceCallback on_sets_ready) {
SetFirstPartySetsConfig(std::move(on_sets_ready));
}
void FirstPartySetsComponentInstallerPolicy::OnRegistrationComplete() {
SetFirstPartySetsConfig(std::move(on_sets_ready_));
}
FirstPartySetsComponentInstallerPolicy::FirstPartySetsComponentInstallerPolicy(
SetsReadyOnceCallback on_sets_ready)
: on_sets_ready_(std::move(on_sets_ready)) {}
FirstPartySetsComponentInstallerPolicy::
~FirstPartySetsComponentInstallerPolicy() = default;
const char
FirstPartySetsComponentInstallerPolicy::kDogfoodInstallerAttributeName[] =
"_internal_experimental_sets";
const char FirstPartySetsComponentInstallerPolicy::kV2FormatOptIn[] =
"_v2_format_plz";
bool FirstPartySetsComponentInstallerPolicy::
SupportsGroupPolicyEnabledComponentUpdates() const {
return true;
}
bool FirstPartySetsComponentInstallerPolicy::RequiresNetworkEncryption() const {
// Update checks and pings associated with this component do not require
// confidentiality, since the component is identical for all users.
return false;
}
update_client::CrxInstaller::Result
FirstPartySetsComponentInstallerPolicy::OnCustomInstall(
const base::Value& manifest,
const base::FilePath& install_dir) {
return update_client::CrxInstaller::Result(0); // Nothing custom here.
}
void FirstPartySetsComponentInstallerPolicy::OnCustomUninstall() {}
base::FilePath FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
const base::FilePath& base) {
return base.Append(kFirstPartySetsSetsFileName);
}
void FirstPartySetsComponentInstallerPolicy::ComponentReady(
const base::Version& version,
const base::FilePath& install_dir,
base::Value manifest) {
if (install_dir.empty() || !GetConfigPathInstance().empty())
return;
VLOG(1) << "First-Party Sets Component ready, version " << version.GetString()
<< " in " << install_dir.value();
GetConfigPathInstance() = GetInstalledPath(install_dir);
SetFirstPartySetsConfig(std::move(on_sets_ready_));
}
// Called during startup and installation before ComponentReady().
bool FirstPartySetsComponentInstallerPolicy::VerifyInstallation(
const base::Value& manifest,
const base::FilePath& install_dir) const {
// No need to actually validate the sets here, since we'll do the validation
// in the Network Service.
return base::PathExists(GetInstalledPath(install_dir));
}
base::FilePath FirstPartySetsComponentInstallerPolicy::GetRelativeInstallDir()
const {
return base::FilePath(kFirstPartySetsRelativeInstallDir);
}
void FirstPartySetsComponentInstallerPolicy::GetHash(
std::vector<uint8_t>* hash) const {
hash->assign(kFirstPartySetsPublicKeySHA256,
kFirstPartySetsPublicKeySHA256 +
base::size(kFirstPartySetsPublicKeySHA256));
}
std::string FirstPartySetsComponentInstallerPolicy::GetName() const {
return kFirstPartySetsManifestName;
}
update_client::InstallerAttributes
FirstPartySetsComponentInstallerPolicy::GetInstallerAttributes() const {
return {
{
kDogfoodInstallerAttributeName,
BoolToString(net::features::kFirstPartySetsIsDogfooder.Get()),
},
{
kV2FormatOptIn,
BoolToString(base::FeatureList::IsEnabled(
net::features::kFirstPartySetsV2ComponentFormat)),
},
};
}
// static
void FirstPartySetsComponentInstallerPolicy::ResetForTesting() {
GetConfigPathInstance().clear();
}
void RegisterFirstPartySetsComponent(ComponentUpdateService* cus) {
VLOG(1) << "Registering First-Party Sets component.";
auto policy = std::make_unique<FirstPartySetsComponentInstallerPolicy>(
/*on_sets_ready=*/base::BindOnce([](base::File sets_file) {
VLOG(1) << "Received First-Party Sets";
content::GetNetworkService()->SetFirstPartySets(std::move(sets_file));
}));
FirstPartySetsComponentInstallerPolicy* raw_policy = policy.get();
// Dereferencing `raw_policy` this way is safe because the closure is invoked
// by the ComponentInstaller instance, which owns `policy` (so they have the
// same lifetime). Therefore if/when the closure is invoked, `policy` is still
// alive.
base::MakeRefCounted<ComponentInstaller>(std::move(policy))
->Register(cus, base::BindOnce(
[](FirstPartySetsComponentInstallerPolicy* policy) {
policy->OnRegistrationComplete();
},
raw_policy));
}
// static
void FirstPartySetsComponentInstallerPolicy::WriteComponentForTesting(
const base::FilePath& install_dir,
base::StringPiece contents) {
CHECK(base::WriteFile(GetInstalledPath(install_dir), contents));
GetConfigPathInstance() = GetInstalledPath(install_dir);
}
} // namespace component_updater