chromium/mojo/core/ipcz_driver/transport.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "mojo/core/ipcz_driver/transport.h"

#include <utility>
#include <vector>

#include "base/check_op.h"
#include "base/functional/overloaded.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/process.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "mojo/core/ipcz_driver/data_pipe.h"
#include "mojo/core/ipcz_driver/invitation.h"
#include "mojo/core/ipcz_driver/object.h"
#include "mojo/core/ipcz_driver/shared_buffer.h"
#include "mojo/core/ipcz_driver/transmissible_platform_handle.h"
#include "mojo/core/ipcz_driver/wrapped_platform_handle.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/platform/platform_handle.h"
#include "third_party/abseil-cpp/absl/container/inlined_vector.h"
#include "third_party/ipcz/include/ipcz/ipcz.h"

#if BUILDFLAG(IS_WIN)
#include "mojo/public/cpp/platform/platform_handle_security_util_win.h"
#endif

namespace mojo::core::ipcz_driver {

namespace {

#if BUILDFLAG(IS_WIN)
// Objects encode their Windows handles as 64-bit HANDLE values within messages.
//
// Windows does not provide any out-of-band mechanism for transmitting objects
// between processes (such as sendmsg() ancillary data on POSIX). Instead there
// is only the DuplicateHandle() API which allows processes to clone object
// handles to or from each others' processes. Therefore every handle
// transmission requires at least one end of the Transport to have sufficient
// access rights and resources to use DuplicateHandle() in reference to handles
// in the other endpoint's process. Handles may be transmitted either as valid
// values from the sender's own process; or as valid values in the recipient's
// process, already duplicated there by the sender.
//
// In general senders always send handles owned by the recipient if capable of
// doing so. If sent handles are owned by the sender, they can only be decoded
// if the recipient itself is sufficiently privileged and capable to duplicate
// handles from the sending process.
//
// This enumeration indicates whether handle values encoded by a serialized
// object belong to the sender or the recipient.
enum HandleOwner : uint8_t {
  // Encoded HANDLEs belong to the sending process. For the recipient to use
  // these, they must have a handle to the sending process with access rights to
  // duplicate handles from there.
  kSender,

  // Encoded HANDLEs belong to the recipient's process. The recipient can use
  // these handles as-is. Only brokers should be trusted to send handles that
  // already belong to the recipient.
  kRecipient,
};

// HANDLE value size varies by architecture. We always encode them with 64 bits.
using HandleData = uint64_t;

HandleData HandleToData(HANDLE handle) {
  return static_cast<HandleData>(reinterpret_cast<uintptr_t>(handle));
}

HANDLE DataToHandle(HandleData data) {
  return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(data));
}
#endif

// Header serialized at the beginning of all mojo-ipcz driver objects.
struct IPCZ_ALIGN(8) ObjectHeader {};

// Header for a serialized Transport object.
struct IPCZ_ALIGN(8) TransportHeader {};

#if BUILDFLAG(IS_WIN)
// Encodes a Windows HANDLE value for transmission within a serialized driver
// object payload. See documentation on HandleOwner above for general notes
// about how handles are communicated over IPC on Windows. Returns true on
// success, with the encoded handle value in `out_handle_data`. Returns false if
// handle duplication failed.
bool EncodeHandle(PlatformHandle& handle,
                  const base::Process& remote_process,
                  HandleOwner handle_owner,
                  HandleData& out_handle_data,
                  bool is_remote_process_untrusted) {
  // Duplicating INVALID_HANDLE_VALUE passes a process handle. If you intend to
  // do this, you must open a valid process handle, not pass the result of
  // GetCurrentProcess(). e.g. https://crbug.com/243339.
  CHECK(handle.is_valid());
  if (handle_owner == HandleOwner::kSender) {
    // Nothing to do when sending handles that belong to us. The recipient must
    // be sufficiently privileged and equipped to duplicate such handles to
    // itself.
    out_handle_data = HandleToData(handle.ReleaseHandle());
    return true;
  }

  // To encode a handle that already belongs to the recipient, we must first
  // duplicate the handle to the recipient's process. Note that it is invalid to
  // call EncodeHandle() to encode kRecipient handles without providing a valid
  // handle to the remote process.
  DCHECK_EQ(handle_owner, HandleOwner::kRecipient);
  DCHECK(remote_process.IsValid());
#if BUILDFLAG(IS_WIN)
  if (is_remote_process_untrusted) {
    DcheckIfFileHandleIsUnsafe(handle.GetHandle().get());
  }
#endif

  HANDLE new_handle;
  if (!::DuplicateHandle(::GetCurrentProcess(), handle.ReleaseHandle(),
                         remote_process.Handle(), &new_handle, 0, FALSE,
                         DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
    return false;
  }

  out_handle_data = HandleToData(new_handle);
  return true;
}

// Decodes a Windows HANDLE value from a transmission containing a serialized
// driver object. See documentation on HandleOwner above for general notes about
// how handles are communicated over IPC on Windows.
PlatformHandle DecodeHandle(HandleData data,
                            const base::Process& remote_process,
                            HandleOwner handle_owner,
                            Transport& from_transport) {
  const HANDLE handle = DataToHandle(data);
  if (handle_owner == HandleOwner::kRecipient) {
    if (from_transport.destination_type() != Transport::kBroker &&
        !from_transport.is_peer_trusted() && !remote_process.is_current()) {
      // Do not trust non-broker endpoints to send handles which already belong
      // to us, unless the transport is explicitly marked as trustworthy (e.g.
      // is connected to a known elevated process.)
      return PlatformHandle();
    }
    return PlatformHandle(base::win::ScopedHandle(handle));
  }

  if (!remote_process.IsValid()) {
    return PlatformHandle();
  }

  HANDLE local_dupe = INVALID_HANDLE_VALUE;
  ::DuplicateHandle(remote_process.Handle(), handle, ::GetCurrentProcess(),
                    &local_dupe, 0, FALSE,
                    DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  return PlatformHandle(base::win::ScopedHandle(local_dupe));
}
#endif  // BUILDFLAG(IS_WIN)

scoped_refptr<base::SingleThreadTaskRunner>& GetIOTaskRunnerStorage() {}

}  // namespace

Transport::Transport(EndpointTypes endpoint_types,
                     PlatformChannelEndpoint endpoint,
                     base::Process remote_process,
                     bool is_remote_process_untrusted)
    :{}

// static
scoped_refptr<Transport> Transport::Create(EndpointTypes endpoint_types,
                                           PlatformChannelEndpoint endpoint,
                                           base::Process remote_process,
                                           bool is_remote_process_untrusted) {}

// static
std::pair<scoped_refptr<Transport>, scoped_refptr<Transport>>
Transport::CreatePair(EndpointType first_type, EndpointType second_type) {}

Transport::~Transport() {}

// static
void Transport::SetIOTaskRunner(
    scoped_refptr<base::SingleThreadTaskRunner> runner) {}

// static
const scoped_refptr<base::SingleThreadTaskRunner>&
Transport::GetIOTaskRunner() {}

void Transport::OverrideIOTaskRunner(
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {}

void Transport::ReportBadActivity(const std::string& error_message) {}

bool Transport::Activate(IpczHandle transport,
                         IpczTransportActivityHandler activity_handler) {}

bool Transport::Deactivate() {}

bool Transport::Transmit(base::span<const uint8_t> data,
                         base::span<const IpczDriverHandle> handles) {}

IpczResult Transport::SerializeObject(ObjectBase& object,
                                      void* data,
                                      size_t* num_bytes,
                                      IpczDriverHandle* handles,
                                      size_t* num_handles) {}

IpczResult Transport::DeserializeObject(
    base::span<const uint8_t> bytes,
    base::span<const IpczDriverHandle> handles,
    scoped_refptr<ObjectBase>& object) {}

void Transport::Close() {}

bool Transport::IsSerializable() const {}

bool Transport::GetSerializedDimensions(Transport& transmitter,
                                        size_t& num_bytes,
                                        size_t& num_handles) {}

bool Transport::Serialize(Transport& transmitter,
                          base::span<uint8_t> data,
                          base::span<PlatformHandle> handles) {}

// static
scoped_refptr<Transport> Transport::Deserialize(
    Transport& from_transport,
    base::span<const uint8_t> data,
    base::span<PlatformHandle> handles) {}

bool Transport::IsIpczTransport() const {}

void Transport::OnChannelMessage(const void* payload,
                                 size_t payload_size,
                                 std::vector<PlatformHandle> handles) {}

void Transport::OnChannelError(Channel::Error error) {}

void Transport::OnChannelDestroyed() {}

bool Transport::CanTransmitHandles() const {}

bool Transport::ShouldSerializeProcessHandle(Transport& transmitter) const {}

Transport::PendingTransmission::PendingTransmission() = default;

Transport::PendingTransmission::PendingTransmission(PendingTransmission&&) =
    default;

Transport::PendingTransmission& Transport::PendingTransmission::operator=(
    PendingTransmission&&) = default;

Transport::PendingTransmission::~PendingTransmission() = default;

}  // namespace mojo::core::ipcz_driver