chromium/chrome/enterprise_companion/url_loader_factory_provider.cc

// 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 {};

// 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 {};

// Services network requests in this process.
class InProcessURLLoaderFactoryProvider : public URLLoaderFactoryProvider {};

#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) {}

#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