#include "remoting/host/daemon_process.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/process/process.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/values.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "remoting/base/auto_thread.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/scoped_sc_handle_win.h"
#include "remoting/host/base/host_exit_codes.h"
#include "remoting/host/base/screen_resolution.h"
#include "remoting/host/base/switches.h"
#include "remoting/host/branding.h"
#include "remoting/host/desktop_session_win.h"
#include "remoting/host/host_config.h"
#include "remoting/host/host_main.h"
#include "remoting/host/ipc_constants.h"
#include "remoting/host/mojom/remoting_host.mojom.h"
#include "remoting/host/pairing_registry_delegate_win.h"
#include "remoting/host/win/etw_trace_consumer.h"
#include "remoting/host/win/host_event_file_logger.h"
#include "remoting/host/win/host_event_windows_event_logger.h"
#include "remoting/host/win/launch_process_with_token.h"
#include "remoting/host/win/security_descriptor.h"
#include "remoting/host/win/unprivileged_process_delegate.h"
#include "remoting/host/win/worker_process_launcher.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
using base::win::ScopedHandle;
namespace {
constexpr char kEtwTracingThreadName[] = "ETW Trace Consumer";
base::win::ScopedHandle DuplicateRegistryKeyHandle(
const base::win::RegKey& key) {
HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
BOOL result = ::DuplicateHandle(::GetCurrentProcess(),
reinterpret_cast<HANDLE>(key.Handle()),
::GetCurrentProcess(), &duplicate_handle, 0,
FALSE, DUPLICATE_SAME_ACCESS);
if (!result || duplicate_handle == INVALID_HANDLE_VALUE) {
return base::win::ScopedHandle();
}
return base::win::ScopedHandle(duplicate_handle);
}
#if defined(OFFICIAL_BUILD)
constexpr wchar_t kLoggingRegistryKeyName[] =
L"SOFTWARE\\Google\\Chrome Remote Desktop\\logging";
#else
constexpr wchar_t kLoggingRegistryKeyName[] = L"SOFTWARE\\Chromoting\\logging";
#endif
constexpr wchar_t kLogToFileRegistryValue[] = L"LogToFile";
constexpr wchar_t kLogToEventLogRegistryValue[] = L"LogToEventLog";
}
namespace remoting {
class WtsTerminalMonitor;
const char* kCopiedSwitchNames[] = {switches::kV, switches::kVModule};
class DaemonProcessWin : public DaemonProcess {
public:
DaemonProcessWin(scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
scoped_refptr<AutoThreadTaskRunner> io_task_runner,
base::OnceClosure stopped_callback);
DaemonProcessWin(const DaemonProcessWin&) = delete;
DaemonProcessWin& operator=(const DaemonProcessWin&) = delete;
~DaemonProcessWin() override;
void OnChannelConnected(int32_t peer_pid) override;
void OnPermanentError(int exit_code) override;
void OnWorkerProcessStopped() override;
bool OnDesktopSessionAgentAttached(
int terminal_id,
int session_id,
mojo::ScopedMessagePipeHandle desktop_pipe) override;
void ConfigureHostLogging();
protected:
std::unique_ptr<DesktopSession> DoCreateDesktopSession(
int terminal_id,
const ScreenResolution& resolution,
bool virtual_terminal) override;
void DoCrashNetworkProcess(const base::Location& location) override;
void LaunchNetworkProcess() override;
void SendHostConfigToNetworkProcess(
const std::string& serialized_config) override;
void SendTerminalDisconnected(int terminal_id) override;
void DisableAutoStart();
bool InitializePairingRegistry();
bool OpenPairingRegistry();
private:
mojo::core::ScopedIPCSupport ipc_support_;
std::unique_ptr<WorkerProcessLauncher> network_launcher_;
ScopedHandle network_process_;
base::win::RegKey pairing_registry_privileged_key_;
base::win::RegKey pairing_registry_unprivileged_key_;
std::unique_ptr<EtwTraceConsumer> etw_trace_consumer_;
mojo::AssociatedRemote<mojom::DesktopSessionConnectionEvents>
desktop_session_connection_events_;
mojo::AssociatedRemote<mojom::RemotingHostControl> remoting_host_control_;
};
DaemonProcessWin::DaemonProcessWin(
scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
scoped_refptr<AutoThreadTaskRunner> io_task_runner,
base::OnceClosure stopped_callback)
: DaemonProcess(caller_task_runner,
io_task_runner,
std::move(stopped_callback)),
ipc_support_(io_task_runner->task_runner(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST) {}
DaemonProcessWin::~DaemonProcessWin() = default;
void DaemonProcessWin::OnChannelConnected(int32_t peer_pid) {
network_process_.Set(OpenProcess(PROCESS_DUP_HANDLE, false, peer_pid));
if (!network_process_.IsValid()) {
CrashNetworkProcess(FROM_HERE);
return;
}
remoting_host_control_.reset();
network_launcher_->GetRemoteAssociatedInterface(
remoting_host_control_.BindNewEndpointAndPassReceiver());
desktop_session_connection_events_.reset();
network_launcher_->GetRemoteAssociatedInterface(
desktop_session_connection_events_.BindNewEndpointAndPassReceiver());
if (!InitializePairingRegistry()) {
CrashNetworkProcess(FROM_HERE);
return;
}
DaemonProcess::OnChannelConnected(peer_pid);
}
void DaemonProcessWin::OnPermanentError(int exit_code) {
DCHECK(kMinPermanentErrorExitCode <= exit_code &&
exit_code <= kMaxPermanentErrorExitCode);
if (exit_code == kInvalidHostIdExitCode ||
exit_code == kInvalidOauthCredentialsExitCode) {
DisableAutoStart();
}
DaemonProcess::OnPermanentError(exit_code);
}
void DaemonProcessWin::OnWorkerProcessStopped() {
remoting_host_control_.reset();
desktop_session_connection_events_.reset();
DaemonProcess::OnWorkerProcessStopped();
}
bool DaemonProcessWin::OnDesktopSessionAgentAttached(
int terminal_id,
int session_id,
mojo::ScopedMessagePipeHandle desktop_pipe) {
if (desktop_session_connection_events_) {
desktop_session_connection_events_->OnDesktopSessionAgentAttached(
terminal_id, session_id, std::move(desktop_pipe));
}
return true;
}
std::unique_ptr<DesktopSession> DaemonProcessWin::DoCreateDesktopSession(
int terminal_id,
const ScreenResolution& resolution,
bool virtual_terminal) {
DCHECK(caller_task_runner()->BelongsToCurrentThread());
if (virtual_terminal) {
return DesktopSessionWin::CreateForVirtualTerminal(
caller_task_runner(), io_task_runner(), this, terminal_id, resolution);
} else {
return DesktopSessionWin::CreateForConsole(
caller_task_runner(), io_task_runner(), this, terminal_id, resolution);
}
}
void DaemonProcessWin::DoCrashNetworkProcess(const base::Location& location) {
DCHECK(caller_task_runner()->BelongsToCurrentThread());
network_launcher_->Crash(location);
}
void DaemonProcessWin::LaunchNetworkProcess() {
DCHECK(caller_task_runner()->BelongsToCurrentThread());
DCHECK(!network_launcher_);
base::FilePath host_binary;
if (!GetInstalledBinaryPath(kHostBinaryName, &host_binary)) {
Stop();
return;
}
std::unique_ptr<base::CommandLine> target(new base::CommandLine(host_binary));
target->AppendSwitchASCII(kProcessTypeSwitchName, kProcessTypeHost);
target->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kCopiedSwitchNames, std::size(kCopiedSwitchNames));
std::unique_ptr<UnprivilegedProcessDelegate> delegate(
new UnprivilegedProcessDelegate(io_task_runner(), std::move(target)));
network_launcher_ =
std::make_unique<WorkerProcessLauncher>(std::move(delegate), this);
}
void DaemonProcessWin::SendHostConfigToNetworkProcess(
const std::string& serialized_config) {
if (!remoting_host_control_) {
return;
}
LOG_IF(ERROR, !remoting_host_control_.is_connected())
<< "IPC channel not connected. HostConfig message will be dropped.";
absl::optional<base::Value::Dict> config(
HostConfigFromJson(serialized_config));
if (!config.has_value()) {
LOG(ERROR) << "Invalid host config, shutting down.";
OnPermanentError(kInvalidHostConfigurationExitCode);
return;
}
remoting_host_control_->ApplyHostConfig(std::move(config.value()));
}
void DaemonProcessWin::SendTerminalDisconnected(int terminal_id) {
if (desktop_session_connection_events_) {
desktop_session_connection_events_->OnTerminalDisconnected(terminal_id);
}
}
std::unique_ptr<DaemonProcess> DaemonProcess::Create(
scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
scoped_refptr<AutoThreadTaskRunner> io_task_runner,
base::OnceClosure stopped_callback) {
auto daemon_process = std::make_unique<DaemonProcessWin>(
caller_task_runner, io_task_runner, std::move(stopped_callback));
daemon_process->ConfigureHostLogging();
daemon_process->Initialize();
return std::move(daemon_process);
}
void DaemonProcessWin::DisableAutoStart() {
ScopedScHandle scmanager(
OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
if (!scmanager.IsValid()) {
PLOG(INFO) << "Failed to connect to the service control manager";
return;
}
DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS;
ScopedScHandle service(
OpenService(scmanager.Get(), kWindowsServiceName, desired_access));
if (!service.IsValid()) {
PLOG(INFO) << "Failed to open to the '" << kWindowsServiceName
<< "' service";
return;
}
if (!ChangeServiceConfig(service.Get(),
SERVICE_NO_CHANGE,
SERVICE_DEMAND_START,
SERVICE_NO_CHANGE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr)) {
PLOG(INFO) << "Failed to change the '" << kWindowsServiceName
<< "'service start type to 'manual'";
}
}
bool DaemonProcessWin::InitializePairingRegistry() {
if (!pairing_registry_privileged_key_.Valid()) {
if (!OpenPairingRegistry()) {
return false;
}
}
base::win::ScopedHandle privileged_key =
DuplicateRegistryKeyHandle(pairing_registry_privileged_key_);
base::win::ScopedHandle unprivileged_key =
DuplicateRegistryKeyHandle(pairing_registry_unprivileged_key_);
if (!(privileged_key.IsValid() && unprivileged_key.IsValid())) {
return false;
}
if (!remoting_host_control_) {
return false;
}
remoting_host_control_->InitializePairingRegistry(
mojo::PlatformHandle(std::move(privileged_key)),
mojo::PlatformHandle(std::move(unprivileged_key)));
return true;
}
bool DaemonProcessWin::OpenPairingRegistry() {
DCHECK(!pairing_registry_privileged_key_.Valid());
DCHECK(!pairing_registry_unprivileged_key_.Valid());
base::win::RegKey root;
DWORD disposition;
LONG result =
root.CreateWithDisposition(HKEY_LOCAL_MACHINE, kPairingRegistryKeyName,
&disposition, KEY_READ | KEY_CREATE_SUB_KEY);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName;
return false;
}
if (disposition == REG_CREATED_NEW_KEY) {
LOG(WARNING) << "Created pairing registry root key which was absent.";
}
base::win::RegKey unprivileged;
result = unprivileged.CreateWithDisposition(
root.Handle(), kPairingRegistryClientsKeyName, &disposition,
KEY_READ | KEY_WRITE);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
<< "\\" << kPairingRegistryClientsKeyName;
return false;
}
if (disposition == REG_CREATED_NEW_KEY) {
LOG(WARNING) << "Created pairing registry client key which was absent.";
}
base::win::RegKey privileged;
result = privileged.Open(root.Handle(), kPairingRegistrySecretsKeyName,
KEY_READ | KEY_WRITE);
if (result == ERROR_FILE_NOT_FOUND) {
LOG(WARNING) << "Pairing registry privileged key absent, creating.";
std::string security_descriptor = "O:BAG:BAD:(A;;GA;;;BA)(A;;GA;;;SY)";
ScopedSd sd = ConvertSddlToSd(security_descriptor);
if (!sd) {
PLOG(ERROR) << "Failed to create a security descriptor for the pairing"
<< "registry privileged key.";
return false;
}
SECURITY_ATTRIBUTES security_attributes = {0};
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = sd.get();
security_attributes.bInheritHandle = FALSE;
HKEY key = nullptr;
result = ::RegCreateKeyEx(root.Handle(), kPairingRegistrySecretsKeyName, 0,
nullptr, 0, KEY_READ | KEY_WRITE,
&security_attributes, &key, &disposition);
privileged.Set(key);
}
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
<< "\\" << kPairingRegistrySecretsKeyName;
return false;
}
pairing_registry_privileged_key_.Set(privileged.Take());
pairing_registry_unprivileged_key_.Set(unprivileged.Take());
return true;
}
void DaemonProcessWin::ConfigureHostLogging() {
DCHECK(!etw_trace_consumer_);
base::win::RegKey logging_reg_key;
LONG result = logging_reg_key.Open(HKEY_LOCAL_MACHINE,
kLoggingRegistryKeyName, KEY_READ);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to open HKLM\\" << kLoggingRegistryKeyName;
return;
}
std::vector<std::unique_ptr<HostEventLogger>> loggers;
if (logging_reg_key.HasValue(kLogToFileRegistryValue)) {
DWORD enabled = 0;
result = logging_reg_key.ReadValueDW(kLogToFileRegistryValue, &enabled);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to read HKLM\\" << kLoggingRegistryKeyName << "\\"
<< kLogToFileRegistryValue;
} else if (enabled) {
auto file_logger = HostEventFileLogger::Create();
if (file_logger) {
loggers.push_back(std::move(file_logger));
}
}
}
if (logging_reg_key.HasValue(kLogToEventLogRegistryValue)) {
DWORD enabled = 0;
result = logging_reg_key.ReadValueDW(kLogToEventLogRegistryValue, &enabled);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to read HKLM\\" << kLoggingRegistryKeyName << "\\"
<< kLogToEventLogRegistryValue;
} else if (enabled) {
auto event_logger = HostEventWindowsEventLogger::Create();
if (event_logger) {
loggers.push_back(std::move(event_logger));
}
}
}
if (loggers.empty()) {
VLOG(1) << "No host event loggers have been configured.";
return;
}
etw_trace_consumer_ = EtwTraceConsumer::Create(
AutoThread::CreateWithType(kEtwTracingThreadName, caller_task_runner(),
base::MessagePumpType::IO),
std::move(loggers));
}
}