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

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

Issue 9476017: Chromoting service now launches the host process in the session the is currently attached to the ph… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cosmetic. Created 8 years, 9 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
« no previous file with comments | « remoting/host/wts_session_process_launcher_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 // 4 //
5 // This file implements the Windows service controlling Me2Me host processes 5 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions. 6 // running within user sessions.
7 7
8 #include "remoting/host/wts_session_process_launcher_win.h" 8 #include "remoting/host/wts_session_process_launcher_win.h"
9 9
10 #include <windows.h> 10 #include <windows.h>
11 11
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/win/scoped_handle.h"
13 15
14 #include "remoting/host/wts_console_monitor_win.h" 16 #include "remoting/host/wts_console_monitor_win.h"
15 17
18 using base::win::ScopedHandle;
19 using base::TimeDelta;
20
21 namespace {
22
23 // The minimum and maximum delays between attempts to inject host process into
24 // a session.
25 const int kMaxLaunchDelaySeconds = 60;
26 const int kMinLaunchDelaySeconds = 1;
27
28 // Name of the default session desktop.
29 const char kDefaultDesktopName[] = "winsta0\\default";
30
31 // Takes the process token and makes a copy of it. The returned handle will have
32 // |desired_access| rights.
33 bool CopyProcessToken(DWORD desired_access,
34 ScopedHandle* token_out) {
35
36 HANDLE handle;
37 if (!OpenProcessToken(GetCurrentProcess(),
38 TOKEN_DUPLICATE | desired_access,
39 &handle)) {
40 LOG_GETLASTERROR(ERROR) << "Failed to open process token";
41 return false;
42 }
43
44 ScopedHandle process_token(handle);
45
46 if (!DuplicateTokenEx(process_token,
47 desired_access,
48 NULL,
49 SecurityImpersonation,
50 TokenPrimary,
51 &handle)) {
52 LOG_GETLASTERROR(ERROR) << "Failed to duplicate the process token";
53 return false;
54 }
55
56 token_out->Set(handle);
57 return true;
58 }
59
60 // Creates a copy of the current process with SE_TCB_NAME privilege enabled.
61 bool CreatePrivilegedToken(ScopedHandle* token_out) {
62 ScopedHandle privileged_token;
63 DWORD desired_access = TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE |
64 TOKEN_DUPLICATE | TOKEN_QUERY;
65 if (!CopyProcessToken(desired_access, &privileged_token)) {
66 return false;
67 }
68
69 // Get the LUID for the SE_TCB_NAME privilege.
70 TOKEN_PRIVILEGES state;
71 state.PrivilegeCount = 1;
72 state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
73 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &state.Privileges[0].Luid)) {
74 LOG_GETLASTERROR(ERROR) <<
75 "Failed to lookup the LUID for the SE_TCB_NAME privilege";
76 return false;
77 }
78
79 // Enable the SE_TCB_NAME privilege.
80 if (!AdjustTokenPrivileges(privileged_token, FALSE, &state, 0, NULL, 0)) {
81 LOG_GETLASTERROR(ERROR) <<
82 "Failed to enable SE_TCB_NAME privilege in a token";
83 return false;
84 }
85
86 token_out->Set(privileged_token.Take());
87 return true;
88 }
89
90 // Creates a copy of the current process token for the given |session_id| so
91 // it can be used to launch a process in that session.
92 bool CreateSessionToken(uint32 session_id,
93 ScopedHandle* token_out) {
94
95 ScopedHandle session_token;
96 DWORD desired_access = TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID |
97 TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY;
98 if (!CopyProcessToken(desired_access, &session_token)) {
99 return false;
100 }
101
102 // Change the session ID of the token.
103 DWORD new_session_id = session_id;
104 if (!SetTokenInformation(session_token,
105 TokenSessionId,
106 &new_session_id,
107 sizeof(new_session_id))) {
108 LOG_GETLASTERROR(ERROR) <<
109 "Failed to change session ID of a token";
110 return false;
111 }
112
113 token_out->Set(session_token.Take());
114 return true;
115 }
116
117 // Launches |binary| in the security context of the supplied |user_token|.
118 bool LaunchProcessAsUser(const FilePath& binary,
119 HANDLE user_token,
120 base::Process* process_out) {
121 string16 command_line = binary.value();
122 string16 desktop = ASCIIToUTF16(kDefaultDesktopName);
123
124 PROCESS_INFORMATION process_info;
125 STARTUPINFOW startup_info;
126
127 memset(&startup_info, 0, sizeof(startup_info));
128 startup_info.cb = sizeof(startup_info);
129 startup_info.lpDesktop = const_cast<LPWSTR>(desktop.c_str());
130
131 if (!CreateProcessAsUserW(user_token,
132 command_line.c_str(),
133 const_cast<LPWSTR>(command_line.c_str()),
134 NULL,
135 NULL,
136 FALSE,
137 0,
138 NULL,
139 NULL,
140 &startup_info,
141 &process_info)) {
142 LOG_GETLASTERROR(ERROR) <<
143 "Failed to launch a process with a user token";
144 return false;
145 }
146
147 CloseHandle(process_info.hThread);
148 process_out->set_handle(process_info.hProcess);
149 return true;
150 }
151
152 } // namespace
153
16 namespace remoting { 154 namespace remoting {
17 155
18 WtsSessionProcessLauncher::WtsSessionProcessLauncher( 156 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
19 WtsConsoleMonitor* monitor) : monitor_(monitor) { 157 WtsConsoleMonitor* monitor,
158 const FilePath& host_binary)
159 : host_binary_(host_binary),
160 monitor_(monitor),
161 state_(StateDetached) {
20 monitor_->AddWtsConsoleObserver(this); 162 monitor_->AddWtsConsoleObserver(this);
21 } 163 }
22 164
23 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 165 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
166 DCHECK(state_ == StateDetached);
167 DCHECK(!timer_.IsRunning());
168 DCHECK(process_.handle() == NULL);
169 DCHECK(process_watcher_.GetWatchedObject() == NULL);
170
24 monitor_->RemoveWtsConsoleObserver(this); 171 monitor_->RemoveWtsConsoleObserver(this);
25 } 172 }
26 173
174 void WtsSessionProcessLauncher::LaunchProcess() {
175 DCHECK(state_ == StateStarting);
176 DCHECK(!timer_.IsRunning());
177 DCHECK(process_.handle() == NULL);
178 DCHECK(process_watcher_.GetWatchedObject() == NULL);
179
180 // Try to launch the process and attach an object watcher to the returned
181 // handle so that we get notified when the process terminates.
182 launch_time_ = base::Time::Now();
183 if (LaunchProcessAsUser(host_binary_, session_token_, &process_)) {
184 if (process_watcher_.StartWatching(process_.handle(), this)) {
185 state_ = StateAttached;
186 return;
187 } else {
188 LOG(ERROR) << "Failed to arm the process watcher.";
189 process_.Terminate(0);
190 process_.Close();
191 }
192 }
193
194 // Something went wrong. Try to launch the host again later. The attempts rate
195 // is limited by exponential backoff.
196 launch_backoff_ = std::max(launch_backoff_ * 2,
197 TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
198 launch_backoff_ = std::min(launch_backoff_,
199 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
200 timer_.Start(FROM_HERE, launch_backoff_,
201 this, &WtsSessionProcessLauncher::LaunchProcess);
202 }
203
204 void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) {
205 DCHECK(state_ == StateAttached);
206 DCHECK(!timer_.IsRunning());
207 DCHECK(process_.handle() != NULL);
208 DCHECK(process_watcher_.GetWatchedObject() == NULL);
209
210 // The host process has been terminated for some reason. The handle can now be
211 // closed.
212 process_.Close();
213
214 // Expand the backoff interval if the process has died quickly or reset it if
215 // it was up longer than the maximum backoff delay.
216 base::TimeDelta delta = base::Time::Now() - launch_time_;
217 if (delta < base::TimeDelta() ||
218 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
219 launch_backoff_ = base::TimeDelta();
220 } else {
221 launch_backoff_ = std::max(launch_backoff_ * 2,
222 TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
223 launch_backoff_ = std::min(launch_backoff_,
224 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
225 }
226
227 // Try to restart the host.
228 state_ = StateStarting;
229 timer_.Start(FROM_HERE, launch_backoff_,
230 this, &WtsSessionProcessLauncher::LaunchProcess);
231 }
232
27 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { 233 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
28 // TODO(alexeypa): The code injecting the host process to a session goes here. 234 DCHECK(state_ == StateDetached);
235 DCHECK(!timer_.IsRunning());
236 DCHECK(process_.handle() == NULL);
237 DCHECK(process_watcher_.GetWatchedObject() == NULL);
238
239 // Temporarily enable the SE_TCB_NAME privilege. The privileged token is
240 // created as needed and kept for later reuse.
241 if (privileged_token_.Get() == NULL) {
242 if (!CreatePrivilegedToken(&privileged_token_)) {
243 return;
244 }
245 }
246
247 if (!ImpersonateLoggedOnUser(privileged_token_)) {
248 LOG_GETLASTERROR(ERROR) <<
249 "Failed to impersonate the privileged token";
250 return;
251 }
252
253 // While the SE_TCB_NAME progolege is enabled, create a session token for
254 // the launched process.
255 bool result = CreateSessionToken(session_id, &session_token_);
256
257 // Revert to the default token. The default token is sufficient to call
258 // CreateProcessAsUser() successfully.
259 CHECK(RevertToSelf());
260
261 if (!result)
262 return;
263
264 // Now try to launch the host.
265 state_ = StateStarting;
266 LaunchProcess();
29 } 267 }
30 268
31 void WtsSessionProcessLauncher::OnSessionDetached() { 269 void WtsSessionProcessLauncher::OnSessionDetached() {
32 // TODO(alexeypa): The code terminating the host process goes here. 270 DCHECK(state_ == StateDetached ||
271 state_ == StateStarting ||
272 state_ == StateAttached);
273
274 switch (state_) {
275 case StateDetached:
276 DCHECK(!timer_.IsRunning());
277 DCHECK(process_.handle() == NULL);
278 DCHECK(process_watcher_.GetWatchedObject() == NULL);
279 break;
280
281 case StateStarting:
282 DCHECK(timer_.IsRunning());
283 DCHECK(process_.handle() == NULL);
284 DCHECK(process_watcher_.GetWatchedObject() == NULL);
285
286 timer_.Stop();
287 launch_backoff_ = base::TimeDelta();
288 state_ = StateDetached;
289 break;
290
291 case StateAttached:
292 DCHECK(!timer_.IsRunning());
293 DCHECK(process_.handle() != NULL);
294 DCHECK(process_watcher_.GetWatchedObject() != NULL);
295
296 process_watcher_.StopWatching();
297 process_.Terminate(0);
298 process_.Close();
299 state_ = StateDetached;
300 break;
301 }
33 } 302 }
34 303
35 } // namespace remoting 304 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/wts_session_process_launcher_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698