blob: 52fe1f6aa77d70d6591d56f921852e44cbdc24ad [file] [log] [blame]
// Copyright 2024 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/enterprise_companion/url_loader_factory_provider.h"
#include <memory>
#include <utility>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/sequence_checker.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "build/build_config.h"
#include "chrome/enterprise_companion/enterprise_companion.h"
#include "chrome/enterprise_companion/event_logger.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "net/cert/cert_verifier.h"
#include "net/cert_net/cert_net_fetcher_url_request.h"
#include "net/proxy_resolution/proxy_config_service.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/transitional_url_loader_factory_owner.h"
#if BUILDFLAG(IS_MAC)
#include <sys/types.h>
#include "chrome/enterprise_companion/mac/mac_utils.h"
#endif
namespace enterprise_companion {
namespace {
class URLRequestContextGetter : public net::URLRequestContextGetter {
public:
explicit URLRequestContextGetter(
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
: network_task_runner_(network_task_runner),
proxy_config_service_(
net::ProxyConfigService::CreateSystemProxyConfigService(
network_task_runner)) {}
URLRequestContextGetter(const URLRequestContextGetter&) = delete;
URLRequestContextGetter& operator=(const URLRequestContextGetter&) = delete;
// Overrides for net::URLRequestContextGetter.
net::URLRequestContext* GetURLRequestContext() override {
if (!url_request_context_.get()) {
net::URLRequestContextBuilder builder;
builder.DisableHttpCache();
builder.set_proxy_config_service(std::move(proxy_config_service_));
cert_net_fetcher_ = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
auto cert_verifier = net::CertVerifier::CreateDefault(cert_net_fetcher_);
builder.SetCertVerifier(std::move(cert_verifier));
url_request_context_ = builder.Build();
cert_net_fetcher_->SetURLRequestContext(url_request_context_.get());
}
return url_request_context_.get();
}
scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
const override {
return network_task_runner_;
}
protected:
~URLRequestContextGetter() override { cert_net_fetcher_->Shutdown(); }
private:
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
std::unique_ptr<net::URLRequestContext> url_request_context_;
scoped_refptr<net::CertNetFetcherURLRequest> cert_net_fetcher_;
};
// A URLLoaderFactory which forwards all calls to a remote. This exists as a
// hack to allow a disconnect callback to be configured on a receiver which gets
// bound via `network::SharedURLLoaderFactory::Clone`.
class URLLoaderFactoryProxy final : public network::mojom::URLLoaderFactory {
public:
explicit URLLoaderFactoryProxy(
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote)
: remote_(std::move(pending_remote)) {}
~URLLoaderFactoryProxy() override = default;
// Overrides for network::mojom::URLLoaderFactory.
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> loader,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) final {
return remote_->CreateLoaderAndStart(std::move(loader), request_id, options,
request, std::move(client),
traffic_annotation);
}
void Clone(mojo::PendingReceiver<URLLoaderFactory> receiver) final {
return remote_->Clone(std::move(receiver));
}
private:
mojo::Remote<network::mojom::URLLoaderFactory> remote_;
};
// Services network requests in this process.
class InProcessURLLoaderFactoryProvider : public URLLoaderFactoryProvider {
public:
InProcessURLLoaderFactoryProvider(
base::SequenceBound<EventLoggerCookieHandler> event_logger_cookie_handler,
mojo::PendingReceiver<network::mojom::URLLoaderFactory> pending_receiver,
base::OnceClosure on_disconnect_callback)
: url_loader_factory_owner_(
base::MakeRefCounted<URLRequestContextGetter>(
base::SingleThreadTaskRunner::GetCurrentDefault()),
/*is_trusted=*/true),
event_logger_cookie_handler_(std::move(event_logger_cookie_handler)) {
if (pending_receiver.is_valid()) {
// Bind the incoming receiver to the URL loader factory indirectly
// through a self-owned `URLLoaderFactoryProxy` receiver, allowing a
// disconnect callback to be configured. Without this indirection, a
// disconnect handler can not be set without refactoring //network code.
mojo::PendingRemote<network::mojom::URLLoaderFactory> remote;
url_loader_factory_owner_.GetURLLoaderFactory()->Clone(
remote.InitWithNewPipeAndPassReceiver());
mojo::MakeSelfOwnedReceiver(
std::make_unique<URLLoaderFactoryProxy>(std::move(remote)),
std::move(pending_receiver))
->set_connection_error_handler(
base::BindOnce(std::move(on_disconnect_callback)));
}
if (event_logger_cookie_handler_) {
mojo::PendingRemote<network::mojom::CookieManager>
cookie_manager_pending_remote;
url_loader_factory_owner_.GetNetworkContext()->GetCookieManager(
cookie_manager_pending_remote.InitWithNewPipeAndPassReceiver());
event_logger_cookie_handler_.AsyncCall(&EventLoggerCookieHandler::Init)
.WithArgs(std::move(cookie_manager_pending_remote),
base::DoNothing());
}
}
~InProcessURLLoaderFactoryProvider() override = default;
std::unique_ptr<network::PendingSharedURLLoaderFactory>
GetPendingURLLoaderFactory() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return url_loader_factory_owner_.GetURLLoaderFactory()->Clone();
}
private:
SEQUENCE_CHECKER(sequence_checker_);
network::TransitionalURLLoaderFactoryOwner url_loader_factory_owner_;
base::SequenceBound<EventLoggerCookieHandler> event_logger_cookie_handler_;
};
#if BUILDFLAG(IS_MAC)
// Delegates network requests to a remote process.
class URLLoaderFactoryProviderProxy : public URLLoaderFactoryProvider {
public:
explicit URLLoaderFactoryProviderProxy(
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote,
base::OnceClosure on_disconnect_callback) {
mojo::Remote remote(std::move(pending_remote));
remote.set_disconnect_handler(std::move(on_disconnect_callback));
url_loader_factory_ =
base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
std::move(remote));
}
~URLLoaderFactoryProviderProxy() override = default;
std::unique_ptr<network::PendingSharedURLLoaderFactory>
GetPendingURLLoaderFactory() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return url_loader_factory_->Clone();
}
private:
SEQUENCE_CHECKER(sequence_checker_);
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
};
#endif // BUILDFLAG(IS_MAC)
} // namespace
base::SequenceBound<URLLoaderFactoryProvider>
CreateInProcessUrlLoaderFactoryProvider(
scoped_refptr<base::SingleThreadTaskRunner> net_thread_runner,
base::SequenceBound<EventLoggerCookieHandler> event_logger_cookie_handler,
mojo::PendingReceiver<network::mojom::URLLoaderFactory> pending_receiver,
base::OnceClosure on_disconnect_callback) {
return base::SequenceBound<InProcessURLLoaderFactoryProvider>(
net_thread_runner, std::move(event_logger_cookie_handler),
std::move(pending_receiver), std::move(on_disconnect_callback));
}
#if BUILDFLAG(IS_MAC)
base::SequenceBound<URLLoaderFactoryProvider>
CreateUrlLoaderFactoryProviderProxy(
scoped_refptr<base::SequencedTaskRunner> task_runner,
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote,
base::OnceClosure on_disconnect_callback) {
return base::SequenceBound<URLLoaderFactoryProviderProxy>(
task_runner, std::move(pending_remote),
std::move(on_disconnect_callback));
}
base::SequenceBound<URLLoaderFactoryProvider> CreateOutOfProcessNetWorker(
base::OnceClosure on_disconnect_callback) {
mojo::PlatformChannel channel;
base::LaunchOptions options;
base::FilePath exe_path;
if (!base::PathService::Get(base::FILE_EXE, &exe_path)) {
LOG(ERROR) << "Failed to retrieve the current executable's path.";
return {};
}
base::CommandLine command_line(exe_path);
command_line.AppendSwitch(kNetWorkerSwitch);
// Attempt to execute the network process in the bootstrap context of the
// logged in user to pick up their proxy configuration and authorization. For
// background, see the Apple Documentation Archive's entry on "Bootstrap
// Contexts".
std::optional<uid_t> uid = GuessLoggedInUser();
if (!uid) {
LOG(ERROR)
<< "Could not determine a logged-in user to impersonate for "
"networking. The root bootstrap namespace (in formal Mach kernel "
"terms, the \"startup context\") will be used, which may cause "
"proxy configuration or authorization to fail.";
} else {
command_line.PrependWrapper(
base::StringPrintf("/bin/launchctl asuser %d", *uid));
}
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
base::Process process = base::LaunchProcess(command_line, options);
channel.RemoteProcessLaunchAttempted();
if (!process.IsValid()) {
LOG(ERROR) << "Failed to launch network process.";
return {};
}
mojo::ScopedMessagePipeHandle pipe = mojo::OutgoingInvitation::SendIsolated(
channel.TakeLocalEndpoint(), {}, process.Handle());
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote(
std::move(pipe), network::mojom::URLLoaderFactory::Version_);
if (!pending_remote) {
LOG(ERROR) << "Failed to establish IPC with the network process.";
return {};
}
return CreateUrlLoaderFactoryProviderProxy(
base::SequencedTaskRunner::GetCurrentDefault(), std::move(pending_remote),
std::move(on_disconnect_callback));
}
#endif // BUILDFLAG(IS_MAC)
} // namespace enterprise_companion