// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/host/daemon_process.h" #include #include #include "base/base_switches.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/process/process.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "base/win/registry.h" #include "base/win/scoped_handle.h" #include "base/win/win_util.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" #include "remoting/base/auto_thread_task_runner.h" #include "remoting/base/scoped_sc_handle_win.h" #include "remoting/host/branding.h" #include "remoting/host/chromoting_messages.h" #include "remoting/host/desktop_session_win.h" #include "remoting/host/host_exit_codes.h" #include "remoting/host/host_main.h" #include "remoting/host/ipc_constants.h" #include "remoting/host/pairing_registry_delegate_win.h" #include "remoting/host/screen_resolution.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" using base::win::ScopedHandle; using base::TimeDelta; namespace { // Duplicates |key| into |target_process| and returns the value that can be sent // over IPC. IPC::PlatformFileForTransit GetRegistryKeyForTransit( base::ProcessHandle target_process, const base::win::RegKey& key) { base::PlatformFile handle = reinterpret_cast(key.Handle()); return IPC::GetFileHandleForProcess(handle, target_process, false); } } // namespace namespace remoting { class WtsTerminalMonitor; // The command line parameters that should be copied from the service's command // line to the host process. const char kEnableVp9SwitchName[] = "enable-vp9"; const char* kCopiedSwitchNames[] = { switches::kV, switches::kVModule, kEnableVp9SwitchName }; class DaemonProcessWin : public DaemonProcess { public: DaemonProcessWin( scoped_refptr caller_task_runner, scoped_refptr io_task_runner, const base::Closure& stopped_callback); ~DaemonProcessWin() override; // WorkerProcessIpcDelegate implementation. void OnChannelConnected(int32_t peer_pid) override; void OnPermanentError(int exit_code) override; // DaemonProcess overrides. void SendToNetwork(IPC::Message* message) override; bool OnDesktopSessionAgentAttached( int terminal_id, base::ProcessHandle desktop_process, IPC::PlatformFileForTransit desktop_pipe) override; protected: // DaemonProcess implementation. scoped_ptr DoCreateDesktopSession( int terminal_id, const ScreenResolution& resolution, bool virtual_terminal) override; void DoCrashNetworkProcess( const tracked_objects::Location& location) override; void LaunchNetworkProcess() override; // Changes the service start type to 'manual'. void DisableAutoStart(); // Initializes the pairing registry on the host side by sending // ChromotingDaemonNetworkMsg_InitializePairingRegistry message. bool InitializePairingRegistry(); // Opens the pairing registry keys. bool OpenPairingRegistry(); private: scoped_ptr network_launcher_; // Handle of the network process. ScopedHandle network_process_; base::win::RegKey pairing_registry_privileged_key_; base::win::RegKey pairing_registry_unprivileged_key_; DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); }; DaemonProcessWin::DaemonProcessWin( scoped_refptr caller_task_runner, scoped_refptr io_task_runner, const base::Closure& stopped_callback) : DaemonProcess(caller_task_runner, io_task_runner, stopped_callback) { } DaemonProcessWin::~DaemonProcessWin() { } void DaemonProcessWin::OnChannelConnected(int32_t peer_pid) { // Obtain the handle of the network process. network_process_.Set(OpenProcess(PROCESS_DUP_HANDLE, false, peer_pid)); if (!network_process_.IsValid()) { CrashNetworkProcess(FROM_HERE); return; } if (!InitializePairingRegistry()) { CrashNetworkProcess(FROM_HERE); return; } DaemonProcess::OnChannelConnected(peer_pid); } void DaemonProcessWin::OnPermanentError(int exit_code) { DCHECK(kMinPermanentErrorExitCode <= exit_code && exit_code <= kMaxPermanentErrorExitCode); // Both kInvalidHostIdExitCode and kInvalidOauthCredentialsExitCode are // errors then will never go away with the current config. // Disabling automatic service start until the host is re-enabled and config // updated. if (exit_code == kInvalidHostIdExitCode || exit_code == kInvalidOauthCredentialsExitCode) { DisableAutoStart(); } DaemonProcess::OnPermanentError(exit_code); } void DaemonProcessWin::SendToNetwork(IPC::Message* message) { if (network_launcher_) { network_launcher_->Send(message); } else { delete message; } } bool DaemonProcessWin::OnDesktopSessionAgentAttached( int terminal_id, base::ProcessHandle desktop_process, IPC::PlatformFileForTransit desktop_pipe) { // Prepare |desktop_process| handle for sending over to the network process. base::ProcessHandle desktop_process_for_transit; if (!DuplicateHandle(GetCurrentProcess(), desktop_process, network_process_.Get(), &desktop_process_for_transit, 0, FALSE, DUPLICATE_SAME_ACCESS)) { PLOG(ERROR) << "Failed to duplicate the desktop process handle"; return false; } // |desktop_pipe| is a handle in the desktop process. It will be duplicated // by the network process directly from the desktop process. SendToNetwork(new ChromotingDaemonNetworkMsg_DesktopAttached( terminal_id, desktop_process_for_transit, desktop_pipe)); return true; } scoped_ptr 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 tracked_objects::Location& location) { DCHECK(caller_task_runner()->BelongsToCurrentThread()); network_launcher_->Crash(location); } void DaemonProcessWin::LaunchNetworkProcess() { DCHECK(caller_task_runner()->BelongsToCurrentThread()); DCHECK(!network_launcher_); // Construct the host binary name. base::FilePath host_binary; if (!GetInstalledBinaryPath(kHostBinaryName, &host_binary)) { Stop(); return; } scoped_ptr target(new base::CommandLine(host_binary)); target->AppendSwitchASCII(kProcessTypeSwitchName, kProcessTypeHost); target->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), kCopiedSwitchNames, arraysize(kCopiedSwitchNames)); scoped_ptr delegate( new UnprivilegedProcessDelegate(io_task_runner(), std::move(target))); network_launcher_.reset(new WorkerProcessLauncher(std::move(delegate), this)); } scoped_ptr DaemonProcess::Create( scoped_refptr caller_task_runner, scoped_refptr io_task_runner, const base::Closure& stopped_callback) { scoped_ptr daemon_process( new DaemonProcessWin(caller_task_runner, io_task_runner, stopped_callback)); 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; } // Change the service start type to 'manual'. All |nullptr| parameters below // mean that there is no change to the corresponding service parameter. 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; } // Duplicate handles to the network process. IPC::PlatformFileForTransit privileged_key = GetRegistryKeyForTransit( network_process_.Get(), pairing_registry_privileged_key_); IPC::PlatformFileForTransit unprivileged_key = GetRegistryKeyForTransit( network_process_.Get(), pairing_registry_unprivileged_key_); if (!(privileged_key && unprivileged_key)) return false; // Initialize the pairing registry in the network process. This has to be done // before the host configuration is sent, otherwise the host will not use // the passed handles. SendToNetwork(new ChromotingDaemonNetworkMsg_InitializePairingRegistry( privileged_key, unprivileged_key)); return true; } // A chromoting top crasher revealed that the pairing registry keys sometimes // cannot be opened. The speculation is that those keys are absent for some // reason. To reduce the host crashes we create those keys here if they are // absent. See crbug.com/379360 for details. bool DaemonProcessWin::OpenPairingRegistry() { DCHECK(!pairing_registry_privileged_key_.Valid()); DCHECK(!pairing_registry_unprivileged_key_.Valid()); // Open the root of the pairing registry. Create if absent. 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."; // Open the pairing registry clients key. Create if 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."; // Open the pairing registry secret key. 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."; // Create a security descriptor that gives full access to local system and // administrators and denies access by anyone else. 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; } } // namespace remoting