1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "remoting/host/win/launch_native_messaging_host_process.h"
6
7 #include <windows.h>
8 #include <shellapi.h>
9
10 #include <cstdint>
11 #include <string>
12
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/win/win_util.h"
21 #include "ipc/ipc_channel.h"
22 #include "remoting/host/switches.h"
23 #include "remoting/host/win/security_descriptor.h"
24
25 namespace {
26
27 // Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
28 const uint32_t kBufferSize = 0;
29 const int kTimeOutMilliseconds = 2000;
30 const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
31
CreateNamedPipe(const std::string & pipe_name,const remoting::ScopedSd & security_descriptor,uint32_t open_mode,base::win::ScopedHandle * file_handle)32 uint32_t CreateNamedPipe(const std::string& pipe_name,
33 const remoting::ScopedSd& security_descriptor,
34 uint32_t open_mode,
35 base::win::ScopedHandle* file_handle) {
36 DCHECK(file_handle);
37
38 SECURITY_ATTRIBUTES security_attributes = {0};
39 security_attributes.nLength = sizeof(security_attributes);
40 security_attributes.lpSecurityDescriptor = security_descriptor.get();
41 security_attributes.bInheritHandle = FALSE;
42
43 base::win::ScopedHandle temp_handle(::CreateNamedPipe(
44 base::ASCIIToUTF16(pipe_name).c_str(), open_mode,
45 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS, 1,
46 kBufferSize, kBufferSize, kTimeOutMilliseconds, &security_attributes));
47
48 if (!temp_handle.IsValid()) {
49 uint32_t error = GetLastError();
50 PLOG(ERROR) << "Failed to create named pipe '" << pipe_name << "'";
51 return error;
52 }
53
54 file_handle->Set(temp_handle.Take());
55 return 0;
56 }
57
58 } // namespace
59
60 namespace remoting {
61
LaunchNativeMessagingHostProcess(const base::FilePath & binary_path,intptr_t parent_window_handle,bool elevate_process,base::win::ScopedHandle * read_handle,base::win::ScopedHandle * write_handle)62 ProcessLaunchResult LaunchNativeMessagingHostProcess(
63 const base::FilePath& binary_path,
64 intptr_t parent_window_handle,
65 bool elevate_process,
66 base::win::ScopedHandle* read_handle,
67 base::win::ScopedHandle* write_handle) {
68 DCHECK(read_handle);
69 DCHECK(write_handle);
70
71 if (!base::PathExists(binary_path)) {
72 LOG(ERROR) << "Cannot find binary: " << binary_path.value();
73 return PROCESS_LAUNCH_RESULT_FAILED;
74 }
75
76 // presubmit: allow wstring
77 std::wstring user_sid;
78 if (!base::win::GetUserSidString(&user_sid)) {
79 LOG(ERROR) << "Failed to query the current user SID.";
80 return PROCESS_LAUNCH_RESULT_FAILED;
81 }
82
83 // Create a security descriptor that gives full access to the caller and
84 // BUILTIN_ADMINISTRATORS and denies access by anyone else.
85 // Local admins need access because the privileged host process will run
86 // as a local admin which may not be the same user as the current user.
87 std::string user_sid_ascii = base::UTF16ToASCII(user_sid);
88 std::string security_descriptor = base::StringPrintf(
89 "O:%sG:%sD:(A;;GA;;;%s)(A;;GA;;;BA)", user_sid_ascii.c_str(),
90 user_sid_ascii.c_str(), user_sid_ascii.c_str());
91
92 ScopedSd sd = ConvertSddlToSd(security_descriptor);
93 if (!sd) {
94 PLOG(ERROR) << "Failed to create a security descriptor.";
95 return PROCESS_LAUNCH_RESULT_FAILED;
96 }
97
98 uint32_t result;
99 std::string input_pipe_name(kChromePipeNamePrefix);
100 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
101 base::win::ScopedHandle temp_write_handle;
102 result = CreateNamedPipe(input_pipe_name, sd, PIPE_ACCESS_OUTBOUND,
103 &temp_write_handle);
104 if (!temp_write_handle.IsValid()) {
105 return PROCESS_LAUNCH_RESULT_FAILED;
106 }
107
108 std::string output_pipe_name(kChromePipeNamePrefix);
109 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
110 base::win::ScopedHandle temp_read_handle;
111 result = CreateNamedPipe(output_pipe_name, sd, PIPE_ACCESS_INBOUND,
112 &temp_read_handle);
113 if (!temp_read_handle.IsValid()) {
114 return PROCESS_LAUNCH_RESULT_FAILED;
115 }
116
117 const base::CommandLine* current_command_line =
118 base::CommandLine::ForCurrentProcess();
119
120 // Create the child process command line by copying switches from the current
121 // command line.
122 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
123
124 DCHECK(!current_command_line->HasSwitch(kElevateSwitchName));
125 // Always add the elevation switch when launched via this function.
126 command_line.AppendSwitch(kElevateSwitchName);
127 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
128 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
129
130 for (const auto& switch_data : current_command_line->GetSwitches()) {
131 command_line.AppendSwitchNative(switch_data.first, switch_data.second);
132 }
133 for (const auto& arg : current_command_line->GetArgs()) {
134 command_line.AppendArgNative(arg);
135 }
136
137 // Get the parameters for the binary to launch.
138 base::CommandLine::StringType params = command_line.GetCommandLineString();
139
140 // Launch the child process, requesting elevation if needed.
141 SHELLEXECUTEINFO info;
142 memset(&info, 0, sizeof(info));
143 info.cbSize = sizeof(info);
144 info.hwnd = reinterpret_cast<HWND>(parent_window_handle);
145 info.lpFile = binary_path.value().c_str();
146 info.lpParameters = params.c_str();
147 info.nShow = SW_HIDE;
148
149 if (elevate_process) {
150 info.lpVerb = L"runas";
151 }
152
153 if (!ShellExecuteEx(&info)) {
154 uint32_t error = GetLastError();
155 PLOG(ERROR) << "Unable to launch '" << binary_path.value() << "'";
156 if (error == ERROR_CANCELLED) {
157 return PROCESS_LAUNCH_RESULT_CANCELLED;
158 } else {
159 return PROCESS_LAUNCH_RESULT_FAILED;
160 }
161 }
162
163 if (!ConnectNamedPipe(temp_write_handle.Get(), nullptr)) {
164 uint32_t error = GetLastError();
165 if (error != ERROR_PIPE_CONNECTED) {
166 PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
167 return PROCESS_LAUNCH_RESULT_FAILED;
168 }
169 }
170
171 if (!ConnectNamedPipe(temp_read_handle.Get(), nullptr)) {
172 uint32_t error = GetLastError();
173 if (error != ERROR_PIPE_CONNECTED) {
174 PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
175 return PROCESS_LAUNCH_RESULT_FAILED;
176 }
177 }
178
179 read_handle->Set(temp_read_handle.Take());
180 write_handle->Set(temp_write_handle.Take());
181 return PROCESS_LAUNCH_RESULT_SUCCESS;
182 }
183
184 } // namespace remoting
185