Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / host / win / unprivileged_process_delegate.cc
blobf9afd6ccf47ed2ca0cbfe56f2173a0da07fd9fdf
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_util.h"
29 #include "remoting/host/switches.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.GetRestrictedToken(token_out) == ERROR_SUCCESS)
104 return true;
106 return false;
109 // Creates a window station with a given name and the default desktop giving
110 // the complete access to |logon_sid|.
111 bool CreateWindowStationAndDesktop(ScopedSid logon_sid,
112 WindowStationAndDesktop* handles_out) {
113 // Convert the logon SID into a string.
114 std::string logon_sid_string = ConvertSidToString(logon_sid.get());
115 if (logon_sid_string.empty()) {
116 PLOG(ERROR) << "Failed to convert a SID to string";
117 return false;
120 // Format the security descriptors in SDDL form.
121 std::string desktop_sddl =
122 base::StringPrintf(kDesktopSdFormat, logon_sid_string.c_str());
123 std::string window_station_sddl =
124 base::StringPrintf(kWindowStationSdFormat, logon_sid_string.c_str(),
125 logon_sid_string.c_str());
127 // The worker runs at low integrity level. Make sure it will be able to attach
128 // to the window station and desktop.
129 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
130 desktop_sddl += kLowIntegrityMandatoryLabel;
131 window_station_sddl += kLowIntegrityMandatoryLabel;
134 // Create the desktop and window station security descriptors.
135 ScopedSd desktop_sd = ConvertSddlToSd(desktop_sddl);
136 ScopedSd window_station_sd = ConvertSddlToSd(window_station_sddl);
137 if (!desktop_sd || !window_station_sd) {
138 PLOG(ERROR) << "Failed to create a security descriptor.";
139 return false;
142 // GetProcessWindowStation() returns the current handle which does not need to
143 // be freed.
144 HWINSTA current_window_station = GetProcessWindowStation();
146 // Generate a unique window station name.
147 std::string window_station_name = base::StringPrintf(
148 "chromoting-%d-%d",
149 base::GetCurrentProcId(),
150 base::RandInt(1, std::numeric_limits<int>::max()));
152 // Make sure that a new window station will be created instead of opening
153 // an existing one.
154 DWORD window_station_flags = 0;
155 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
156 window_station_flags = CWF_CREATE_ONLY;
158 // Request full access because this handle will be inherited by the worker
159 // process which needs full access in order to attach to the window station.
160 DWORD desired_access =
161 WINSTA_ALL_ACCESS | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
163 SECURITY_ATTRIBUTES security_attributes = {0};
164 security_attributes.nLength = sizeof(security_attributes);
165 security_attributes.lpSecurityDescriptor = window_station_sd.get();
166 security_attributes.bInheritHandle = TRUE;
168 WindowStationAndDesktop handles;
169 handles.SetWindowStation(CreateWindowStation(
170 base::UTF8ToUTF16(window_station_name).c_str(), window_station_flags,
171 desired_access, &security_attributes));
172 if (!handles.window_station()) {
173 PLOG(ERROR) << "CreateWindowStation() failed";
174 return false;
177 // Switch to the new window station and create a desktop on it.
178 if (!SetProcessWindowStation(handles.window_station())) {
179 PLOG(ERROR) << "SetProcessWindowStation() failed";
180 return false;
183 desired_access = DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW |
184 DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD |
185 DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS |
186 DESKTOP_SWITCHDESKTOP | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
188 security_attributes.nLength = sizeof(security_attributes);
189 security_attributes.lpSecurityDescriptor = desktop_sd.get();
190 security_attributes.bInheritHandle = TRUE;
192 // The default desktop of the interactive window station is called "Default".
193 // Name the created desktop the same way in case any code relies on that.
194 // The desktop name should not make any difference though.
195 handles.SetDesktop(CreateDesktop(L"Default", nullptr, nullptr, 0,
196 desired_access, &security_attributes));
198 // Switch back to the original window station.
199 if (!SetProcessWindowStation(current_window_station)) {
200 PLOG(ERROR) << "SetProcessWindowStation() failed";
201 return false;
204 if (!handles.desktop()) {
205 PLOG(ERROR) << "CreateDesktop() failed";
206 return false;
209 handles.Swap(*handles_out);
210 return true;
213 } // namespace
215 UnprivilegedProcessDelegate::UnprivilegedProcessDelegate(
216 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
217 scoped_ptr<base::CommandLine> target_command)
218 : io_task_runner_(io_task_runner),
219 target_command_(target_command.Pass()),
220 event_handler_(nullptr) {
223 UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() {
224 DCHECK(CalledOnValidThread());
225 DCHECK(!channel_);
226 DCHECK(!worker_process_.IsValid());
229 void UnprivilegedProcessDelegate::LaunchProcess(
230 WorkerProcessLauncher* event_handler) {
231 DCHECK(CalledOnValidThread());
232 DCHECK(!event_handler_);
234 event_handler_ = event_handler;
236 scoped_ptr<IPC::ChannelProxy> server;
238 // Create a restricted token that will be used to run the worker process.
239 ScopedHandle token;
240 if (!CreateRestrictedToken(&token)) {
241 PLOG(ERROR) << "Failed to create a restricted LocalService token";
242 ReportFatalError();
243 return;
246 // Determine our logon SID, so we can grant it access to our window station
247 // and desktop.
248 ScopedSid logon_sid = GetLogonSid(token.Get());
249 if (!logon_sid) {
250 PLOG(ERROR) << "Failed to retrieve the logon SID";
251 ReportFatalError();
252 return;
255 // Create the process and thread security descriptors.
256 ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd);
257 ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd);
258 if (!process_sd || !thread_sd) {
259 PLOG(ERROR) << "Failed to create a security descriptor";
260 ReportFatalError();
261 return;
264 SECURITY_ATTRIBUTES process_attributes;
265 process_attributes.nLength = sizeof(process_attributes);
266 process_attributes.lpSecurityDescriptor = process_sd.get();
267 process_attributes.bInheritHandle = FALSE;
269 SECURITY_ATTRIBUTES thread_attributes;
270 thread_attributes.nLength = sizeof(thread_attributes);
271 thread_attributes.lpSecurityDescriptor = thread_sd.get();
272 thread_attributes.bInheritHandle = FALSE;
274 ScopedHandle worker_process;
276 // Take a lock why any inheritable handles are open to make sure that only
277 // one process inherits them.
278 base::AutoLock lock(g_inherit_handles_lock.Get());
280 // Create a connected IPC channel.
281 base::File client;
282 if (!CreateConnectedIpcChannel(io_task_runner_, this, &client, &server)) {
283 ReportFatalError();
284 return;
287 // Convert the handle value into a decimal integer. Handle values are 32bit
288 // even on 64bit platforms.
289 std::string pipe_handle = base::StringPrintf(
290 "%d", reinterpret_cast<ULONG_PTR>(client.GetPlatformFile()));
292 // Pass the IPC channel via the command line.
293 base::CommandLine command_line(target_command_->argv());
294 command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle);
296 // Create our own window station and desktop accessible by |logon_sid|.
297 WindowStationAndDesktop handles;
298 if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) {
299 PLOG(ERROR) << "Failed to create a window station and desktop";
300 ReportFatalError();
301 return;
304 // Try to launch the worker process. The launched process inherits
305 // the window station, desktop and pipe handles, created above.
306 ScopedHandle worker_thread;
307 if (!LaunchProcessWithToken(command_line.GetProgram(),
308 command_line.GetCommandLineString(),
309 token.Get(),
310 &process_attributes,
311 &thread_attributes,
312 true,
314 nullptr,
315 &worker_process,
316 &worker_thread)) {
317 ReportFatalError();
318 return;
322 channel_ = server.Pass();
323 ReportProcessLaunched(worker_process.Pass());
326 void UnprivilegedProcessDelegate::Send(IPC::Message* message) {
327 DCHECK(CalledOnValidThread());
329 if (channel_) {
330 channel_->Send(message);
331 } else {
332 delete message;
336 void UnprivilegedProcessDelegate::CloseChannel() {
337 DCHECK(CalledOnValidThread());
339 channel_.reset();
342 void UnprivilegedProcessDelegate::KillProcess() {
343 DCHECK(CalledOnValidThread());
345 channel_.reset();
346 event_handler_ = nullptr;
348 if (worker_process_.IsValid()) {
349 TerminateProcess(worker_process_.Get(), CONTROL_C_EXIT);
350 worker_process_.Close();
354 bool UnprivilegedProcessDelegate::OnMessageReceived(
355 const IPC::Message& message) {
356 DCHECK(CalledOnValidThread());
358 return event_handler_->OnMessageReceived(message);
361 void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) {
362 DCHECK(CalledOnValidThread());
364 DWORD pid = GetProcessId(worker_process_.Get());
365 if (pid != static_cast<DWORD>(peer_pid)) {
366 LOG(ERROR) << "The actual client PID " << pid
367 << " does not match the one reported by the client: "
368 << peer_pid;
369 ReportFatalError();
370 return;
373 event_handler_->OnChannelConnected(peer_pid);
376 void UnprivilegedProcessDelegate::OnChannelError() {
377 DCHECK(CalledOnValidThread());
379 event_handler_->OnChannelError();
382 void UnprivilegedProcessDelegate::ReportFatalError() {
383 DCHECK(CalledOnValidThread());
385 channel_.reset();
387 WorkerProcessLauncher* event_handler = event_handler_;
388 event_handler_ = nullptr;
389 event_handler->OnFatalError();
392 void UnprivilegedProcessDelegate::ReportProcessLaunched(
393 base::win::ScopedHandle worker_process) {
394 DCHECK(CalledOnValidThread());
395 DCHECK(!worker_process_.IsValid());
397 worker_process_ = worker_process.Pass();
399 // Report a handle that can be used to wait for the worker process completion,
400 // query information about the process and duplicate handles.
401 DWORD desired_access =
402 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
403 HANDLE temp_handle;
404 if (!DuplicateHandle(GetCurrentProcess(),
405 worker_process_.Get(),
406 GetCurrentProcess(),
407 &temp_handle,
408 desired_access,
409 FALSE,
410 0)) {
411 PLOG(ERROR) << "Failed to duplicate a handle";
412 ReportFatalError();
413 return;
415 ScopedHandle limited_handle(temp_handle);
417 event_handler_->OnProcessLaunched(limited_handle.Pass());
420 } // namespace remoting