Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / remoting / host / win / unprivileged_process_delegate.cc
blob0537218ad23421bd2be3f67feeabe96ba575c58f
2 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // This file implements the Windows service controlling Me2Me host processes
7 // running within user sessions.
9 #include "remoting/host/win/unprivileged_process_delegate.h"
11 #include <sddl.h>
13 #include "base/command_line.h"
14 #include "base/files/file.h"
15 #include "base/logging.h"
16 #include "base/rand_util.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/synchronization/lock.h"
22 #include "base/win/scoped_handle.h"
23 #include "base/win/windows_version.h"
24 #include "ipc/ipc_channel.h"
25 #include "ipc/ipc_channel_proxy.h"
26 #include "ipc/ipc_message.h"
27 #include "remoting/base/typed_buffer.h"
28 #include "remoting/host/ipc_constants.h"
29 #include "remoting/host/ipc_util.h"
30 #include "remoting/host/win/launch_process_with_token.h"
31 #include "remoting/host/win/security_descriptor.h"
32 #include "remoting/host/win/window_station_and_desktop.h"
33 #include "sandbox/win/src/restricted_token.h"
35 using base::win::ScopedHandle;
37 namespace remoting {
39 namespace {
41 // The security descriptors below are used to lock down access to the worker
42 // process launched by UnprivilegedProcessDelegate. UnprivilegedProcessDelegate
43 // assumes that it runs under SYSTEM. The worker process is launched under
44 // a different account and attaches to a newly created window station. If UAC is
45 // supported by the OS, the worker process is started at low integrity level.
46 // UnprivilegedProcessDelegate replaces the first printf parameter in
47 // the strings below by the logon SID assigned to the worker process.
49 // Security descriptor of the desktop the worker process attaches to. It gives
50 // SYSTEM and the logon SID full access to the desktop.
51 const char kDesktopSdFormat[] = "O:SYG:SYD:(A;;0xf01ff;;;SY)(A;;0xf01ff;;;%s)";
53 // Mandatory label specifying low integrity level.
54 const char kLowIntegrityMandatoryLabel[] = "S:(ML;CIOI;NW;;;LW)";
56 // Security descriptor of the window station the worker process attaches to. It
57 // gives SYSTEM and the logon SID full access the window station. The child
58 // containers and objects inherit ACE giving SYSTEM and the logon SID full
59 // access to them as well.
60 const char kWindowStationSdFormat[] = "O:SYG:SYD:(A;CIOIIO;GA;;;SY)"
61 "(A;CIOIIO;GA;;;%s)(A;NP;0xf037f;;;SY)(A;NP;0xf037f;;;%s)";
63 // Security descriptor of the worker process. It gives access SYSTEM full access
64 // to the process. It gives READ_CONTROL, SYNCHRONIZE, PROCESS_QUERY_INFORMATION
65 // and PROCESS_TERMINATE rights to the built-in administrators group.
66 const char kWorkerProcessSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120401;;;BA)";
68 // Security descriptor of the worker process threads. It gives access SYSTEM
69 // full access to the threads. It gives READ_CONTROL, SYNCHRONIZE,
70 // THREAD_QUERY_INFORMATION and THREAD_TERMINATE rights to the built-in
71 // administrators group.
72 const char kWorkerThreadSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120801;;;BA)";
74 // Creates a token with limited access that will be used to run the worker
75 // process.
76 bool CreateRestrictedToken(ScopedHandle* token_out) {
77 // Create a token representing LocalService account.
78 HANDLE temp_handle;
79 if (!LogonUser(L"LocalService", L"NT AUTHORITY", nullptr,
80 LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT,
81 &temp_handle)) {
82 return false;
84 ScopedHandle token(temp_handle);
86 sandbox::RestrictedToken restricted_token;
87 if (restricted_token.Init(token.Get()) != ERROR_SUCCESS)
88 return false;
90 // Remove all privileges in the token.
91 if (restricted_token.DeleteAllPrivileges(nullptr) != ERROR_SUCCESS)
92 return false;
94 // Set low integrity level if supported by the OS.
95 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
96 if (restricted_token.SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW)
97 != ERROR_SUCCESS) {
98 return false;
102 // Return the resulting token.
103 if (restricted_token.GetRestrictedTokenHandle(&temp_handle) ==
104 ERROR_SUCCESS) {
105 token_out->Set(temp_handle);
106 return true;
108 return false;
111 // Creates a window station with a given name and the default desktop giving
112 // the complete access to |logon_sid|.
113 bool CreateWindowStationAndDesktop(ScopedSid logon_sid,
114 WindowStationAndDesktop* handles_out) {
115 // Convert the logon SID into a string.
116 std::string logon_sid_string = ConvertSidToString(logon_sid.get());
117 if (logon_sid_string.empty()) {
118 PLOG(ERROR) << "Failed to convert a SID to string";
119 return false;
122 // Format the security descriptors in SDDL form.
123 std::string desktop_sddl =
124 base::StringPrintf(kDesktopSdFormat, logon_sid_string.c_str());
125 std::string window_station_sddl =
126 base::StringPrintf(kWindowStationSdFormat, logon_sid_string.c_str(),
127 logon_sid_string.c_str());
129 // The worker runs at low integrity level. Make sure it will be able to attach
130 // to the window station and desktop.
131 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
132 desktop_sddl += kLowIntegrityMandatoryLabel;
133 window_station_sddl += kLowIntegrityMandatoryLabel;
136 // Create the desktop and window station security descriptors.
137 ScopedSd desktop_sd = ConvertSddlToSd(desktop_sddl);
138 ScopedSd window_station_sd = ConvertSddlToSd(window_station_sddl);
139 if (!desktop_sd || !window_station_sd) {
140 PLOG(ERROR) << "Failed to create a security descriptor.";
141 return false;
144 // GetProcessWindowStation() returns the current handle which does not need to
145 // be freed.
146 HWINSTA current_window_station = GetProcessWindowStation();
148 // Generate a unique window station name.
149 std::string window_station_name = base::StringPrintf(
150 "chromoting-%d-%d",
151 base::GetCurrentProcId(),
152 base::RandInt(1, std::numeric_limits<int>::max()));
154 // Make sure that a new window station will be created instead of opening
155 // an existing one.
156 DWORD window_station_flags = 0;
157 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
158 window_station_flags = CWF_CREATE_ONLY;
160 // Request full access because this handle will be inherited by the worker
161 // process which needs full access in order to attach to the window station.
162 DWORD desired_access =
163 WINSTA_ALL_ACCESS | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
165 SECURITY_ATTRIBUTES security_attributes = {0};
166 security_attributes.nLength = sizeof(security_attributes);
167 security_attributes.lpSecurityDescriptor = window_station_sd.get();
168 security_attributes.bInheritHandle = TRUE;
170 WindowStationAndDesktop handles;
171 handles.SetWindowStation(CreateWindowStation(
172 base::UTF8ToUTF16(window_station_name).c_str(), window_station_flags,
173 desired_access, &security_attributes));
174 if (!handles.window_station()) {
175 PLOG(ERROR) << "CreateWindowStation() failed";
176 return false;
179 // Switch to the new window station and create a desktop on it.
180 if (!SetProcessWindowStation(handles.window_station())) {
181 PLOG(ERROR) << "SetProcessWindowStation() failed";
182 return false;
185 desired_access = DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW |
186 DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD |
187 DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS |
188 DESKTOP_SWITCHDESKTOP | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
190 security_attributes.nLength = sizeof(security_attributes);
191 security_attributes.lpSecurityDescriptor = desktop_sd.get();
192 security_attributes.bInheritHandle = TRUE;
194 // The default desktop of the interactive window station is called "Default".
195 // Name the created desktop the same way in case any code relies on that.
196 // The desktop name should not make any difference though.
197 handles.SetDesktop(CreateDesktop(L"Default", nullptr, nullptr, 0,
198 desired_access, &security_attributes));
200 // Switch back to the original window station.
201 if (!SetProcessWindowStation(current_window_station)) {
202 PLOG(ERROR) << "SetProcessWindowStation() failed";
203 return false;
206 if (!handles.desktop()) {
207 PLOG(ERROR) << "CreateDesktop() failed";
208 return false;
211 handles.Swap(*handles_out);
212 return true;
215 } // namespace
217 UnprivilegedProcessDelegate::UnprivilegedProcessDelegate(
218 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
219 scoped_ptr<base::CommandLine> target_command)
220 : io_task_runner_(io_task_runner),
221 event_handler_(nullptr),
222 target_command_(target_command.Pass()) {
225 UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() {
226 DCHECK(CalledOnValidThread());
227 DCHECK(!channel_);
228 DCHECK(!worker_process_.IsValid());
231 void UnprivilegedProcessDelegate::LaunchProcess(
232 WorkerProcessLauncher* event_handler) {
233 DCHECK(CalledOnValidThread());
234 DCHECK(!event_handler_);
236 event_handler_ = event_handler;
238 scoped_ptr<IPC::ChannelProxy> server;
240 // Create a restricted token that will be used to run the worker process.
241 ScopedHandle token;
242 if (!CreateRestrictedToken(&token)) {
243 PLOG(ERROR) << "Failed to create a restricted LocalService token";
244 ReportFatalError();
245 return;
248 // Determine our logon SID, so we can grant it access to our window station
249 // and desktop.
250 ScopedSid logon_sid = GetLogonSid(token.Get());
251 if (!logon_sid) {
252 PLOG(ERROR) << "Failed to retrieve the logon SID";
253 ReportFatalError();
254 return;
257 // Create the process and thread security descriptors.
258 ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd);
259 ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd);
260 if (!process_sd || !thread_sd) {
261 PLOG(ERROR) << "Failed to create a security descriptor";
262 ReportFatalError();
263 return;
266 SECURITY_ATTRIBUTES process_attributes;
267 process_attributes.nLength = sizeof(process_attributes);
268 process_attributes.lpSecurityDescriptor = process_sd.get();
269 process_attributes.bInheritHandle = FALSE;
271 SECURITY_ATTRIBUTES thread_attributes;
272 thread_attributes.nLength = sizeof(thread_attributes);
273 thread_attributes.lpSecurityDescriptor = thread_sd.get();
274 thread_attributes.bInheritHandle = FALSE;
276 ScopedHandle worker_process;
278 // Take a lock why any inheritable handles are open to make sure that only
279 // one process inherits them.
280 base::AutoLock lock(g_inherit_handles_lock.Get());
282 // Create a connected IPC channel.
283 base::File client;
284 if (!CreateConnectedIpcChannel(io_task_runner_, this, &client, &server)) {
285 ReportFatalError();
286 return;
289 // Convert the handle value into a decimal integer. Handle values are 32bit
290 // even on 64bit platforms.
291 std::string pipe_handle = base::StringPrintf(
292 "%d", reinterpret_cast<ULONG_PTR>(client.GetPlatformFile()));
294 // Pass the IPC channel via the command line.
295 base::CommandLine command_line(target_command_->argv());
296 command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle);
298 // Create our own window station and desktop accessible by |logon_sid|.
299 WindowStationAndDesktop handles;
300 if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) {
301 PLOG(ERROR) << "Failed to create a window station and desktop";
302 ReportFatalError();
303 return;
306 // Try to launch the worker process. The launched process inherits
307 // the window station, desktop and pipe handles, created above.
308 ScopedHandle worker_thread;
309 if (!LaunchProcessWithToken(command_line.GetProgram(),
310 command_line.GetCommandLineString(),
311 token.Get(),
312 &process_attributes,
313 &thread_attributes,
314 true,
316 nullptr,
317 &worker_process,
318 &worker_thread)) {
319 ReportFatalError();
320 return;
324 channel_ = server.Pass();
325 ReportProcessLaunched(worker_process.Pass());
328 void UnprivilegedProcessDelegate::Send(IPC::Message* message) {
329 DCHECK(CalledOnValidThread());
331 if (channel_) {
332 channel_->Send(message);
333 } else {
334 delete message;
338 void UnprivilegedProcessDelegate::CloseChannel() {
339 DCHECK(CalledOnValidThread());
341 channel_.reset();
344 void UnprivilegedProcessDelegate::KillProcess() {
345 DCHECK(CalledOnValidThread());
347 channel_.reset();
348 event_handler_ = nullptr;
350 if (worker_process_.IsValid()) {
351 TerminateProcess(worker_process_.Get(), CONTROL_C_EXIT);
352 worker_process_.Close();
356 bool UnprivilegedProcessDelegate::OnMessageReceived(
357 const IPC::Message& message) {
358 DCHECK(CalledOnValidThread());
360 return event_handler_->OnMessageReceived(message);
363 void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) {
364 DCHECK(CalledOnValidThread());
366 DWORD pid = GetProcessId(worker_process_.Get());
367 if (pid != static_cast<DWORD>(peer_pid)) {
368 LOG(ERROR) << "The actual client PID " << pid
369 << " does not match the one reported by the client: "
370 << peer_pid;
371 ReportFatalError();
372 return;
375 event_handler_->OnChannelConnected(peer_pid);
378 void UnprivilegedProcessDelegate::OnChannelError() {
379 DCHECK(CalledOnValidThread());
381 event_handler_->OnChannelError();
384 void UnprivilegedProcessDelegate::ReportFatalError() {
385 DCHECK(CalledOnValidThread());
387 channel_.reset();
389 WorkerProcessLauncher* event_handler = event_handler_;
390 event_handler_ = nullptr;
391 event_handler->OnFatalError();
394 void UnprivilegedProcessDelegate::ReportProcessLaunched(
395 base::win::ScopedHandle worker_process) {
396 DCHECK(CalledOnValidThread());
397 DCHECK(!worker_process_.IsValid());
399 worker_process_ = worker_process.Pass();
401 // Report a handle that can be used to wait for the worker process completion,
402 // query information about the process and duplicate handles.
403 DWORD desired_access =
404 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
405 HANDLE temp_handle;
406 if (!DuplicateHandle(GetCurrentProcess(),
407 worker_process_.Get(),
408 GetCurrentProcess(),
409 &temp_handle,
410 desired_access,
411 FALSE,
412 0)) {
413 PLOG(ERROR) << "Failed to duplicate a handle";
414 ReportFatalError();
415 return;
417 ScopedHandle limited_handle(temp_handle);
419 event_handler_->OnProcessLaunched(limited_handle.Pass());
422 } // namespace remoting