Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(399)

Side by Side Diff: remoting/host/launch_process_in_session_win.cc

Issue 10832068: Moving Windows-only files: remoting/host -> remoting/host/win. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/launch_process_in_session_win.h"
6
7 #include <windows.h>
8 #include <winternl.h>
9
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/scoped_native_library.h"
13 #include "base/stringprintf.h"
14 #include "base/utf_string_conversions.h"
15 #include "base/win/scoped_handle.h"
16 #include "base/win/scoped_process_information.h"
17 #include "base/win/windows_version.h"
18
19 using base::win::ScopedHandle;
20
21 namespace {
22
23 const wchar_t kCreateProcessDefaultPipeNameFormat[] =
24 L"\\\\.\\Pipe\\TerminalServer\\SystemExecSrvr\\%d";
25
26 // Undocumented WINSTATIONINFOCLASS value causing
27 // winsta!WinStationQueryInformationW() to return the name of the pipe for
28 // requesting cross-session process creation.
29 const WINSTATIONINFOCLASS kCreateProcessPipeNameClass =
30 static_cast<WINSTATIONINFOCLASS>(0x21);
31
32 const int kPipeBusyWaitTimeoutMs = 2000;
33 const int kPipeConnectMaxAttempts = 3;
34
35 // The minimum and maximum delays between attempts to inject host process into
36 // a session.
37 const int kMaxLaunchDelaySeconds = 60;
38 const int kMinLaunchDelaySeconds = 1;
39
40 // Name of the default session desktop.
41 wchar_t kDefaultDesktopName[] = L"winsta0\\default";
42
43 // Requests the execution server to create a process in the specified session
44 // using the default (i.e. Winlogon) token. This routine relies on undocumented
45 // OS functionality and will likely not work on anything but XP or W2K3.
46 bool CreateRemoteSessionProcess(
47 uint32 session_id,
48 const std::wstring& application_name,
49 const std::wstring& command_line,
50 PROCESS_INFORMATION* process_information_out)
51 {
52 DCHECK(base::win::GetVersion() == base::win::VERSION_XP);
53
54 std::wstring pipe_name;
55
56 // Use winsta!WinStationQueryInformationW() to determine the process creation
57 // pipe name for the session.
58 FilePath winsta_path(base::GetNativeLibraryName(UTF8ToUTF16("winsta")));
59 base::ScopedNativeLibrary winsta(winsta_path);
60 if (winsta.is_valid()) {
61 PWINSTATIONQUERYINFORMATIONW win_station_query_information =
62 static_cast<PWINSTATIONQUERYINFORMATIONW>(
63 winsta.GetFunctionPointer("WinStationQueryInformationW"));
64 if (win_station_query_information) {
65 wchar_t name[MAX_PATH];
66 ULONG name_length;
67 if (win_station_query_information(0,
68 session_id,
69 kCreateProcessPipeNameClass,
70 name,
71 sizeof(name),
72 &name_length)) {
73 pipe_name.assign(name);
74 }
75 }
76 }
77
78 // Use the default pipe name if we couldn't query its name.
79 if (pipe_name.empty()) {
80 pipe_name = StringPrintf(kCreateProcessDefaultPipeNameFormat, session_id);
81 }
82
83 // Try to connect to the named pipe.
84 base::win::ScopedHandle pipe;
85 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
86 pipe.Set(CreateFile(pipe_name.c_str(),
87 GENERIC_READ | GENERIC_WRITE,
88 0,
89 NULL,
90 OPEN_EXISTING,
91 0,
92 NULL));
93 if (pipe.IsValid()) {
94 break;
95 }
96
97 // Cannot continue retrying if error is something other than
98 // ERROR_PIPE_BUSY.
99 if (GetLastError() != ERROR_PIPE_BUSY) {
100 break;
101 }
102
103 // Cannot continue retrying if wait on pipe fails.
104 if (!WaitNamedPipe(pipe_name.c_str(), kPipeBusyWaitTimeoutMs)) {
105 break;
106 }
107 }
108
109 if (!pipe.IsValid()) {
110 LOG_GETLASTERROR(ERROR) << "Failed to connect to '" << pipe_name << "'";
111 return false;
112 }
113
114 std::wstring desktop_name(kDefaultDesktopName);
115
116 // |CreateProcessRequest| structure passes the same parameters to
117 // the execution server as CreateProcessAsUser() function does. Strings are
118 // stored as wide strings immediately after the structure. String pointers are
119 // represented as byte offsets to string data from the beginning of
120 // the structure.
121 struct CreateProcessRequest {
122 DWORD size;
123 DWORD process_id;
124 BOOL use_default_token;
125 HANDLE token;
126 LPWSTR application_name;
127 LPWSTR command_line;
128 SECURITY_ATTRIBUTES process_attributes;
129 SECURITY_ATTRIBUTES thread_attributes;
130 BOOL inherit_handles;
131 DWORD creation_flags;
132 LPVOID environment;
133 LPWSTR current_directory;
134 STARTUPINFOW startup_info;
135 PROCESS_INFORMATION process_information;
136 };
137
138 // Allocate a large enough buffer to hold the CreateProcessRequest structure
139 // and three NULL-terminated string parameters.
140 size_t size = sizeof(CreateProcessRequest) + sizeof(wchar_t) *
141 (application_name.size() + command_line.size() + desktop_name.size() + 3);
142 scoped_array<char> buffer(new char[size]);
143 memset(buffer.get(), 0, size);
144
145 // Marshal the input parameters.
146 CreateProcessRequest* request =
147 reinterpret_cast<CreateProcessRequest*>(buffer.get());
148 request->size = size;
149 request->use_default_token = TRUE;
150 request->process_id = GetCurrentProcessId();
151 request->startup_info.cb = sizeof(request->startup_info);
152
153 size_t buffer_offset = sizeof(CreateProcessRequest);
154
155 request->application_name = reinterpret_cast<LPWSTR>(buffer_offset);
156 std::copy(application_name.begin(),
157 application_name.end(),
158 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
159 buffer_offset += (application_name.size() + 1) * sizeof(wchar_t);
160
161 request->command_line = reinterpret_cast<LPWSTR>(buffer_offset);
162 std::copy(command_line.begin(),
163 command_line.end(),
164 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
165 buffer_offset += (command_line.size() + 1) * sizeof(wchar_t);
166
167 request->startup_info.lpDesktop =
168 reinterpret_cast<LPWSTR>(buffer_offset);
169 std::copy(desktop_name.begin(),
170 desktop_name.end(),
171 reinterpret_cast<wchar_t*>(buffer.get() + buffer_offset));
172
173 // Pass the request to create a process in the target session.
174 DWORD bytes;
175 if (!WriteFile(pipe.Get(), buffer.get(), size, &bytes, NULL)) {
176 LOG_GETLASTERROR(ERROR) << "Failed to send CreateProcessAsUser request";
177 return false;
178 }
179
180 // Receive the response.
181 struct CreateProcessResponse {
182 DWORD size;
183 BOOL success;
184 DWORD last_error;
185 PROCESS_INFORMATION process_information;
186 };
187
188 CreateProcessResponse response;
189 if (!ReadFile(pipe.Get(), &response, sizeof(response), &bytes, NULL)) {
190 LOG_GETLASTERROR(ERROR) << "Failed to receive CreateProcessAsUser response";
191 return false;
192 }
193
194 // The server sends the data in one chunk so if we didn't received a complete
195 // answer something bad happend and there is no point in retrying.
196 if (bytes != sizeof(response)) {
197 SetLastError(ERROR_RECEIVE_PARTIAL);
198 return false;
199 }
200
201 if (!response.success) {
202 SetLastError(response.last_error);
203 return false;
204 }
205
206 // The execution server does not return handles to the created process and
207 // thread.
208 if (response.process_information.hProcess == NULL) {
209 // N.B. PROCESS_ALL_ACCESS is different in XP and Vista+ versions of
210 // the SDK. |desired_access| below is effectively PROCESS_ALL_ACCESS from
211 // the XP version of the SDK.
212 DWORD desired_access =
213 STANDARD_RIGHTS_REQUIRED |
214 SYNCHRONIZE |
215 PROCESS_TERMINATE |
216 PROCESS_CREATE_THREAD |
217 PROCESS_SET_SESSIONID |
218 PROCESS_VM_OPERATION |
219 PROCESS_VM_READ |
220 PROCESS_VM_WRITE |
221 PROCESS_DUP_HANDLE |
222 PROCESS_CREATE_PROCESS |
223 PROCESS_SET_QUOTA |
224 PROCESS_SET_INFORMATION |
225 PROCESS_QUERY_INFORMATION |
226 PROCESS_SUSPEND_RESUME;
227 response.process_information.hProcess =
228 OpenProcess(desired_access,
229 FALSE,
230 response.process_information.dwProcessId);
231 if (!response.process_information.hProcess) {
232 LOG_GETLASTERROR(ERROR) << "Failed to open process "
233 << response.process_information.dwProcessId;
234 return false;
235 }
236 }
237
238 *process_information_out = response.process_information;
239 return true;
240 }
241
242 } // namespace
243
244 namespace remoting {
245
246 // Launches |binary| in a different session. The target session is specified by
247 // |user_token|.
248 bool LaunchProcessInSession(const FilePath& binary,
249 const std::wstring& command_line,
250 HANDLE user_token,
251 base::Process* process_out) {
252 std::wstring application_name = binary.value();
253
254 base::win::ScopedProcessInformation process_info;
255 STARTUPINFOW startup_info;
256
257 memset(&startup_info, 0, sizeof(startup_info));
258 startup_info.cb = sizeof(startup_info);
259 startup_info.lpDesktop = kDefaultDesktopName;
260
261 BOOL result = CreateProcessAsUser(user_token,
262 application_name.c_str(),
263 const_cast<LPWSTR>(command_line.c_str()),
264 NULL,
265 NULL,
266 FALSE,
267 0,
268 NULL,
269 NULL,
270 &startup_info,
271 process_info.Receive());
272
273 // CreateProcessAsUser will fail on XP and W2K3 with ERROR_PIPE_NOT_CONNECTED
274 // if the user hasn't logged to the target session yet. In such a case
275 // we try to talk to the execution server directly emulating what
276 // the undocumented and not-exported advapi32!CreateRemoteSessionProcessW()
277 // function does. The created process will run under Winlogon'a token instead
278 // of |user_token|. Since Winlogon runs as SYSTEM, this suits our needs.
279 if (!result &&
280 GetLastError() == ERROR_PIPE_NOT_CONNECTED &&
281 base::win::GetVersion() == base::win::VERSION_XP) {
282 DWORD session_id;
283 DWORD return_length;
284 result = GetTokenInformation(user_token,
285 TokenSessionId,
286 &session_id,
287 sizeof(session_id),
288 &return_length);
289 if (result && session_id != 0) {
290 result = CreateRemoteSessionProcess(session_id,
291 application_name,
292 command_line,
293 process_info.Receive());
294 } else {
295 // Restore the error status returned by CreateProcessAsUser().
296 result = FALSE;
297 SetLastError(ERROR_PIPE_NOT_CONNECTED);
298 }
299 }
300
301 if (!result) {
302 LOG_GETLASTERROR(ERROR) <<
303 "Failed to launch a process with a user token";
304 return false;
305 }
306
307 CHECK(process_info.IsValid());
308 process_out->set_handle(process_info.TakeProcessHandle());
309 return true;
310 }
311
312 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/launch_process_in_session_win.h ('k') | remoting/host/scoped_thread_desktop_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698