chromium/mojo/public/cpp/platform/named_platform_channel_win.cc

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

#include "mojo/public/cpp/platform/named_platform_channel.h"

#include <windows.h>

#include <sddl.h>

#include <memory>

#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/strcat_win.h"
#include "base/strings/string_number_conversions_win.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"

namespace mojo {

namespace {

// A DACL to grant:
// GA = Generic All
// access to:
// SY = LOCAL_SYSTEM
// BA = BUILTIN_ADMINISTRATORS
// OW = OWNER_RIGHTS
constexpr wchar_t kDefaultSecurityDescriptor[] =
    L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)";

}  // namespace

// static
NamedPlatformChannel::ServerName
NamedPlatformChannel::GenerateRandomServerName() {
  return base::StrCat({base::NumberToWString(::GetCurrentProcessId()), L".",
                       base::NumberToWString(::GetCurrentThreadId()), L".",
                       base::NumberToWString(base::RandUint64())});
}

// static
std::wstring NamedPlatformChannel::GetPipeNameFromServerName(
    const NamedPlatformChannel::ServerName& server_name) {
  return L"\\\\.\\pipe\\mojo." + server_name;
}

// static
PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
    const Options& options,
    ServerName* server_name) {
  ServerName name = options.server_name;
  if (name.empty())
    name = GenerateRandomServerName();

  PSECURITY_DESCRIPTOR security_desc = nullptr;
  ULONG security_desc_len = 0;
  PCHECK(::ConvertStringSecurityDescriptorToSecurityDescriptor(
      options.security_descriptor.empty() ? kDefaultSecurityDescriptor
                                          : options.security_descriptor.c_str(),
      SDDL_REVISION_1, &security_desc, &security_desc_len));
  std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree);
  SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
                                             security_desc, FALSE};

  const DWORD kOpenMode = options.enforce_uniqueness
                              ? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
                                    FILE_FLAG_FIRST_PIPE_INSTANCE
                              : PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
  const DWORD kPipeMode =
      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;

  std::wstring pipe_name = GetPipeNameFromServerName(name);
  PlatformHandle handle(base::win::ScopedHandle(::CreateNamedPipeW(
      pipe_name.c_str(), kOpenMode, kPipeMode,
      options.enforce_uniqueness ? 1 : 255,  // Max instances.
      4096,                                  // Out buffer size.
      4096,                                  // In buffer size.
      5000,                                  // Timeout in milliseconds.
      &security_attributes)));

  *server_name = name;
  return PlatformChannelServerEndpoint(std::move(handle));
}

// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
    const Options& options) {
  std::wstring pipe_name = GetPipeNameFromServerName(options.server_name);

  // Note: This may block.
  if (!::WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT))
    return PlatformChannelEndpoint();

  const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
  // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
  // the client.
  const DWORD kFlags =
      SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
  PlatformHandle handle(base::win::ScopedHandle(
      ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, nullptr,
                    OPEN_EXISTING, kFlags, nullptr)));

  // The server may have stopped accepting a connection between the
  // WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is
  // returned.
  DPLOG_IF(ERROR, !handle.is_valid())
      << "Named pipe " << pipe_name
      << " could not be opened after WaitNamedPipe succeeded";
  return PlatformChannelEndpoint(std::move(handle));
}

}  // namespace mojo