Chromium Code Reviews| Index: remoting/host/win/launch_native_messaging_host_process.cc |
| diff --git a/remoting/host/win/launch_native_messaging_host_process.cc b/remoting/host/win/launch_native_messaging_host_process.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..86de11240098f47460c2de240ce2ec1e824023d3 |
| --- /dev/null |
| +++ b/remoting/host/win/launch_native_messaging_host_process.cc |
| @@ -0,0 +1,188 @@ |
| +// Copyright 2016 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/win/launch_native_messaging_host_process.h" |
| + |
| +#include <windows.h> |
| +#include <shellapi.h> |
| + |
| +#include <cstdint> |
| +#include <string> |
| + |
| +#include "base/command_line.h" |
| +#include "base/files/file_path.h" |
| +#include "base/logging.h" |
| +#include "base/macros.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/win/win_util.h" |
| +#include "ipc/ipc_channel.h" |
| +#include "remoting/host/switches.h" |
| +#include "remoting/host/win/security_descriptor.h" |
| + |
| +namespace { |
| + |
| +// Windows will use default buffer size when 0 is passed to CreateNamedPipeW(). |
| +const uint32_t kBufferSize = 0; |
| +const int kTimeOutMilliseconds = 2000; |
| +const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop."; |
| + |
| +const uint32_t kProcessLaunched = 0; |
| +const uint32_t kSecurityDescriptorCreationFailed = 1; |
|
Sergey Ulanov
2016/07/28 23:57:15
This special error code is not mentioned in the he
joedow
2016/07/29 03:32:56
Done.
|
| + |
| +uint32_t CreateNamedPipe(const std::string& pipe_name, |
| + const remoting::ScopedSd& security_descriptor, |
| + uint32_t open_mode, |
| + base::win::ScopedHandle* file_handle) { |
| + DCHECK(file_handle); |
| + |
| + SECURITY_ATTRIBUTES security_attributes = {0}; |
| + security_attributes.nLength = sizeof(security_attributes); |
| + security_attributes.lpSecurityDescriptor = security_descriptor.get(); |
| + security_attributes.bInheritHandle = FALSE; |
| + |
| + base::win::ScopedHandle temp_handle(::CreateNamedPipe( |
| + base::ASCIIToUTF16(pipe_name).c_str(), |
| + open_mode, |
| + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS, |
| + 1, |
| + kBufferSize, |
| + kBufferSize, |
| + kTimeOutMilliseconds, |
| + &security_attributes)); |
| + |
| + if (!temp_handle.IsValid()) { |
| + uint32_t error = GetLastError(); |
| + PLOG(ERROR) << "Failed to create named pipe '" << pipe_name << "'"; |
| + return error; |
| + } |
| + |
| + file_handle->Set(temp_handle.Take()); |
| + return 0; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace remoting { |
| + |
| +uint32_t LaunchNativeMessagingHostProcess( |
| + const base::FilePath& binary_path, |
| + intptr_t parent_window_handle, |
| + bool elevate_process, |
| + base::win::ScopedHandle* read_handle, |
| + base::win::ScopedHandle* write_handle) { |
| + DCHECK(read_handle); |
| + DCHECK(write_handle); |
| + |
| + // presubmit: allow wstring |
| + std::wstring user_sid; |
| + if (!base::win::GetUserSidString(&user_sid)) { |
| + LOG(ERROR) << "Failed to query the current user SID."; |
| + return kSecurityDescriptorCreationFailed; |
| + } |
| + |
| + // Create a security descriptor that gives full access to the caller and |
| + // BUILTIN_ADMINISTRATORS and denies access by anyone else. |
| + // Local admins need access because the privileged host process will run |
| + // as a local admin which may not be the same user as the current user. |
| + std::string user_sid_ascii = base::UTF16ToASCII(user_sid); |
| + std::string security_descriptor = base::StringPrintf( |
| + "O:%sG:%sD:(A;;GA;;;%s)(A;;GA;;;BA)", user_sid_ascii.c_str(), |
| + user_sid_ascii.c_str(), user_sid_ascii.c_str()); |
| + |
| + ScopedSd sd = ConvertSddlToSd(security_descriptor); |
| + if (!sd) { |
| + PLOG(ERROR) << "Failed to create a security descriptor."; |
| + return kSecurityDescriptorCreationFailed; |
| + } |
| + |
| + uint32_t result; |
| + std::string input_pipe_name(kChromePipeNamePrefix); |
| + input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID()); |
| + base::win::ScopedHandle temp_write_handle; |
| + result = CreateNamedPipe(input_pipe_name, sd, PIPE_ACCESS_OUTBOUND, |
| + &temp_write_handle); |
| + if (!temp_write_handle.IsValid()) { |
| + return result; |
| + } |
| + |
| + std::string output_pipe_name(kChromePipeNamePrefix); |
| + output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID()); |
| + base::win::ScopedHandle temp_read_handle; |
| + result = CreateNamedPipe(output_pipe_name, sd, PIPE_ACCESS_INBOUND, |
| + &temp_read_handle); |
| + if (!temp_read_handle.IsValid()) { |
| + return result; |
| + } |
| + |
| + const base::CommandLine* current_command_line = |
| + base::CommandLine::ForCurrentProcess(); |
| + const base::CommandLine::SwitchMap& switches = |
| + current_command_line->GetSwitches(); |
| + base::CommandLine::StringVector args = current_command_line->GetArgs(); |
| + |
| + // Create the child process command line by copying switches from the current |
| + // command line. |
| + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| + |
| + if (elevate_process) { |
| + DCHECK(!current_command_line->HasSwitch(kElevateSwitchName)); |
| + command_line.AppendSwitch(kElevateSwitchName); |
| + } |
| + command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name); |
| + command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name); |
| + |
| + for (base::CommandLine::SwitchMap::const_iterator i = switches.begin(); |
| + i != switches.end(); ++i) { |
| + command_line.AppendSwitchNative(i->first, i->second); |
| + } |
| + for (base::CommandLine::StringVector::const_iterator i = args.begin(); |
|
Sergey Ulanov
2016/07/28 23:57:15
for (const auto& arg : args) ...
joedow
2016/07/29 03:32:56
Good call.
|
| + i != args.end(); ++i) { |
| + command_line.AppendArgNative(*i); |
| + } |
| + |
| + // Get the parameters for the binary to launch. |
| + base::CommandLine::StringType params = command_line.GetCommandLineString(); |
| + |
| + // Launch the child process, requesting elevation if needed. |
| + SHELLEXECUTEINFO info; |
| + memset(&info, 0, sizeof(info)); |
| + info.cbSize = sizeof(info); |
| + info.hwnd = reinterpret_cast<HWND>(parent_window_handle); |
| + info.lpFile = binary_path.value().c_str(); |
| + info.lpParameters = params.c_str(); |
| + info.nShow = SW_HIDE; |
| + |
| + if (elevate_process) { |
| + info.lpVerb = L"runas"; |
| + } |
| + |
| + if (!ShellExecuteEx(&info)) { |
| + uint32_t error = GetLastError(); |
| + PLOG(ERROR) << "Unable to launch '" << binary_path.value() << "'"; |
| + return error; |
| + } |
| + |
| + if (!ConnectNamedPipe(temp_write_handle.Get(), nullptr)) { |
| + uint32_t error = GetLastError(); |
| + if (error != ERROR_PIPE_CONNECTED) { |
| + PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'"; |
| + return error; |
| + } |
| + } |
| + |
| + if (!ConnectNamedPipe(temp_read_handle.Get(), nullptr)) { |
| + uint32_t error = GetLastError(); |
| + if (error != ERROR_PIPE_CONNECTED) { |
| + PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'"; |
| + return error; |
| + } |
| + } |
| + |
| + read_handle->Set(temp_read_handle.Take()); |
| + write_handle->Set(temp_write_handle.Take()); |
| + return kProcessLaunched; |
| +} |
| + |
| +} // namespace remoting |