chromium/chrome/enterprise_companion/app/app_net_worker.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 <sys/errno.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/bind_post_task.h"
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "chrome/enterprise_companion/app/app.h"
#include "chrome/enterprise_companion/enterprise_companion_status.h"
#include "chrome/enterprise_companion/event_logger.h"
#include "chrome/enterprise_companion/url_loader_factory_provider.h"
#include "mojo/public/cpp/bindings/pending_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 "services/network/public/mojom/url_loader_factory.mojom.h"

namespace enterprise_companion {

namespace {

constexpr uid_t kNobodyUid = -2;
constexpr gid_t kNobodyGid = -2;

// AppNetWorker runs networking tasks for the companion app in a dedicated
// process.
class AppNetWorker : public App {
 public:
  AppNetWorker() {
    net_thread_.StartWithOptions({base::MessagePumpType::IO, 0});
  }

  ~AppNetWorker() override {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  }

 private:
  void FirstTaskRun() override {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    // The cookie handler is created before reducing privilege, as opening the
    // cookie file requires root.
    base::SequenceBound<EventLoggerCookieHandler> event_logger_cookie_handler =
        CreateEventLoggerCookieHandler();
    if (!event_logger_cookie_handler) {
      LOG(WARNING) << "Failed to create EventLoggerCookieHandler, logging "
                      "cookies will not be transmitted or persisted.";
    }

    // If running as root, drop down to "nobody".
    if (getuid() == 0) {
      if (setgid(kNobodyGid)) {
        PLOG(ERROR) << "Failed to set gid " << kNobodyGid;
        Shutdown(EnterpriseCompanionStatus::FromPosixErrno(errno));
        return;
      }

      if (setuid(kNobodyUid)) {
        PLOG(ERROR) << "Failed to set uid " << kNobodyUid;
        Shutdown(EnterpriseCompanionStatus::FromPosixErrno(errno));
        return;
      }
    }

    mojo::PlatformChannelEndpoint endpoint =
        mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
            *base::CommandLine::ForCurrentProcess());
    if (!endpoint.is_valid()) {
      Shutdown(
          EnterpriseCompanionStatus(ApplicationError::kMojoConnectionFailed));
      return;
    }

    mojo::ScopedMessagePipeHandle pipe =
        mojo::IncomingInvitation::AcceptIsolated(std::move(endpoint));
    if (!pipe->is_valid()) {
      Shutdown(
          EnterpriseCompanionStatus(ApplicationError::kMojoConnectionFailed));
      return;
    }

    url_loader_factory_provider_ = CreateInProcessUrlLoaderFactoryProvider(
        net_thread_.task_runner(), std::move(event_logger_cookie_handler),
        mojo::PendingReceiver<network::mojom::URLLoaderFactory>(
            std::move(pipe)),
        base::BindPostTaskToCurrentDefault(base::BindOnce(
            &AppNetWorker::Shutdown, weak_ptr_factory_.GetWeakPtr(),
            EnterpriseCompanionStatus(
                ApplicationError::kMojoConnectionFailed))));
  }

  SEQUENCE_CHECKER(sequence_checker_);
  base::Thread net_thread_{"Network"};
  base::SequenceBound<URLLoaderFactoryProvider> url_loader_factory_provider_;
  base::WeakPtrFactory<AppNetWorker> weak_ptr_factory_{this};
};

}  // namespace

std::unique_ptr<App> CreateAppNetWorker() {
  return std::make_unique<AppNetWorker>();
}

}  // namespace enterprise_companion