Only sync parent directory once after a leveldb file rename.
[chromium-blink-merge.git] / remoting / host / win / unprivileged_process_delegate.cc
blob251a06dd9c589b32cb75cfde58f59d029a14072e
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/logging.h"
15 #include "base/process_util.h"
16 #include "base/rand_util.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/string16.h"
19 #include "base/stringprintf.h"
20 #include "base/synchronization/lock.h"
21 #include "base/utf_string_conversions.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/win/launch_process_with_token.h"
30 #include "remoting/host/win/security_descriptor.h"
31 #include "remoting/host/win/window_station_and_desktop.h"
32 #include "sandbox/win/src/restricted_token.h"
34 using base::win::ScopedHandle;
36 namespace remoting {
38 namespace {
40 // The security descriptors below are used to lock down access to the worker
41 // process launched by UnprivilegedProcessDelegate. UnprivilegedProcessDelegate
42 // assumes that it runs under SYSTEM. The worker process is launched under
43 // a different account and attaches to a newly created window station. If UAC is
44 // supported by the OS, the worker process is started at low integrity level.
45 // UnprivilegedProcessDelegate replaces the first printf parameter in
46 // the strings below by the logon SID assigned to the worker process.
48 // Security descriptor used to protect the named pipe in between
49 // CreateNamedPipe() and CreateFile() calls before it is passed to the network
50 // process. It gives full access to LocalSystem and denies access by anyone
51 // else.
52 const char kDaemonIpcSd[] = "O:SYG:SYD:(A;;GA;;;SY)";
54 // Security descriptor of the desktop the worker process attaches to. It gives
55 // SYSTEM and the logon SID full access to the desktop.
56 const char kDesktopSdFormat[] = "O:SYG:SYD:(A;;0xf01ff;;;SY)(A;;0xf01ff;;;%s)";
58 // Mandatory label specifying low integrity level.
59 const char kLowIntegrityMandatoryLabel[] = "S:(ML;CIOI;NW;;;LW)";
61 // Security descriptor of the window station the worker process attaches to. It
62 // gives SYSTEM and the logon SID full access the window station. The child
63 // containers and objects inherit ACE giving SYSTEM and the logon SID full
64 // access to them as well.
65 const char kWindowStationSdFormat[] = "O:SYG:SYD:(A;CIOIIO;GA;;;SY)"
66 "(A;CIOIIO;GA;;;%1$s)(A;NP;0xf037f;;;SY)(A;NP;0xf037f;;;%1$s)";
68 // Security descriptor of the worker process. It gives access SYSTEM full access
69 // to the process. It gives READ_CONTROL, SYNCHRONIZE, PROCESS_QUERY_INFORMATION
70 // and PROCESS_TERMINATE rights to the built-in administrators group.
71 const char kWorkerProcessSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120401;;;BA)";
73 // Security descriptor of the worker process threads. It gives access SYSTEM
74 // full access to the threads. It gives READ_CONTROL, SYNCHRONIZE,
75 // THREAD_QUERY_INFORMATION and THREAD_TERMINATE rights to the built-in
76 // administrators group.
77 const char kWorkerThreadSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120801;;;BA)";
79 // Creates a token with limited access that will be used to run the worker
80 // process.
81 bool CreateRestrictedToken(ScopedHandle* token_out) {
82 // Create a token representing LocalService account.
83 ScopedHandle token;
84 if (!LogonUser(L"LocalService", L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE,
85 LOGON32_PROVIDER_DEFAULT, token.Receive())) {
86 return false;
89 sandbox::RestrictedToken restricted_token;
90 if (restricted_token.Init(token) != ERROR_SUCCESS)
91 return false;
93 // Remove all privileges in the token.
94 if (restricted_token.DeleteAllPrivileges(NULL) != ERROR_SUCCESS)
95 return false;
97 // Set low integrity level if supported by the OS.
98 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
99 if (restricted_token.SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW)
100 != ERROR_SUCCESS) {
101 return false;
105 // Return the resulting token.
106 return restricted_token.GetRestrictedTokenHandle(token_out->Receive()) ==
107 ERROR_SUCCESS;
110 // Creates a window station with a given name and the default desktop giving
111 // the complete access to |logon_sid|.
112 bool CreateWindowStationAndDesktop(ScopedSid logon_sid,
113 WindowStationAndDesktop* handles_out) {
114 // Convert the logon SID into a string.
115 std::string logon_sid_string = ConvertSidToString(logon_sid.get());
116 if (logon_sid_string.empty()) {
117 LOG_GETLASTERROR(ERROR) << "Failed to convert a SID to string";
118 return false;
121 // Format the security descriptors in SDDL form.
122 std::string desktop_sddl =
123 base::StringPrintf(kDesktopSdFormat, logon_sid_string.c_str());
124 std::string window_station_sddl =
125 base::StringPrintf(kWindowStationSdFormat, 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 LOG_GETLASTERROR(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 UTF8ToUTF16(window_station_name).c_str(), window_station_flags,
171 desired_access, &security_attributes));
172 if (!handles.window_station()) {
173 LOG_GETLASTERROR(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 LOG_GETLASTERROR(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", NULL, NULL, 0, desired_access,
196 &security_attributes));
198 // Switch back to the original window station.
199 if (!SetProcessWindowStation(current_window_station)) {
200 LOG_GETLASTERROR(ERROR) << "SetProcessWindowStation() failed";
201 return false;
204 if (!handles.desktop()) {
205 LOG_GETLASTERROR(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<CommandLine> target_command)
218 : io_task_runner_(io_task_runner),
219 event_handler_(NULL),
220 target_command_(target_command.Pass()) {
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 // Generate a unique name for the channel.
239 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
241 // Create a restricted token that will be used to run the worker process.
242 ScopedHandle token;
243 if (!CreateRestrictedToken(&token)) {
244 LOG_GETLASTERROR(ERROR)
245 << "Failed to create a restricted LocalService token";
246 ReportFatalError();
247 return;
250 // Determine our logon SID, so we can grant it access to our window station
251 // and desktop.
252 ScopedSid logon_sid = GetLogonSid(token);
253 if (!logon_sid) {
254 LOG_GETLASTERROR(ERROR) << "Failed to retrieve the logon SID";
255 ReportFatalError();
256 return;
259 // Create the process and thread security descriptors.
260 ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd);
261 ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd);
262 if (!process_sd || !thread_sd) {
263 LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor";
264 ReportFatalError();
265 return;
268 SECURITY_ATTRIBUTES process_attributes;
269 process_attributes.nLength = sizeof(process_attributes);
270 process_attributes.lpSecurityDescriptor = process_sd.get();
271 process_attributes.bInheritHandle = FALSE;
273 SECURITY_ATTRIBUTES thread_attributes;
274 thread_attributes.nLength = sizeof(thread_attributes);
275 thread_attributes.lpSecurityDescriptor = thread_sd.get();
276 thread_attributes.bInheritHandle = FALSE;
278 ScopedHandle worker_process;
280 // Take a lock why any inheritable handles are open to make sure that only
281 // one process inherits them.
282 base::AutoLock lock(g_inherit_handles_lock.Get());
284 // Create a connected IPC channel.
285 ScopedHandle client;
286 if (!CreateConnectedIpcChannel(channel_name, kDaemonIpcSd, io_task_runner_,
287 this, &client, &server)) {
288 ReportFatalError();
289 return;
292 // Convert the handle value into a decimal integer. Handle values are 32bit
293 // even on 64bit platforms.
294 std::string pipe_handle = base::StringPrintf(
295 "%d", reinterpret_cast<ULONG_PTR>(client.Get()));
297 // Pass the IPC channel via the command line.
298 CommandLine command_line(target_command_->argv());
299 command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle);
301 // Create our own window station and desktop accessible by |logon_sid|.
302 WindowStationAndDesktop handles;
303 if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) {
304 LOG_GETLASTERROR(ERROR)
305 << "Failed to create a window station and desktop";
306 ReportFatalError();
307 return;
310 // Try to launch the worker process. The launched process inherits
311 // the window station, desktop and pipe handles, created above.
312 ScopedHandle worker_thread;
313 if (!LaunchProcessWithToken(command_line.GetProgram(),
314 command_line.GetCommandLineString(),
315 token,
316 &process_attributes,
317 &thread_attributes,
318 true,
320 NULL,
321 &worker_process,
322 &worker_thread)) {
323 ReportFatalError();
324 return;
328 channel_ = server.Pass();
329 ReportProcessLaunched(worker_process.Pass());
332 void UnprivilegedProcessDelegate::Send(IPC::Message* message) {
333 DCHECK(CalledOnValidThread());
335 if (channel_) {
336 channel_->Send(message);
337 } else {
338 delete message;
342 void UnprivilegedProcessDelegate::CloseChannel() {
343 DCHECK(CalledOnValidThread());
345 channel_.reset();
348 void UnprivilegedProcessDelegate::KillProcess() {
349 DCHECK(CalledOnValidThread());
351 channel_.reset();
353 if (worker_process_.IsValid()) {
354 TerminateProcess(worker_process_, CONTROL_C_EXIT);
355 worker_process_.Close();
359 bool UnprivilegedProcessDelegate::OnMessageReceived(
360 const IPC::Message& message) {
361 DCHECK(CalledOnValidThread());
363 return event_handler_->OnMessageReceived(message);
366 void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) {
367 DCHECK(CalledOnValidThread());
369 DWORD pid = GetProcessId(worker_process_);
370 if (pid != static_cast<DWORD>(peer_pid)) {
371 LOG(ERROR) << "The actual client PID " << pid
372 << " does not match the one reported by the client: "
373 << peer_pid;
374 ReportFatalError();
375 return;
378 event_handler_->OnChannelConnected(peer_pid);
381 void UnprivilegedProcessDelegate::OnChannelError() {
382 DCHECK(CalledOnValidThread());
384 event_handler_->OnChannelError();
387 void UnprivilegedProcessDelegate::ReportFatalError() {
388 DCHECK(CalledOnValidThread());
390 channel_.reset();
392 WorkerProcessLauncher* event_handler = event_handler_;
393 event_handler_ = NULL;
394 event_handler->OnFatalError();
397 void UnprivilegedProcessDelegate::ReportProcessLaunched(
398 base::win::ScopedHandle worker_process) {
399 DCHECK(CalledOnValidThread());
400 DCHECK(!worker_process_.IsValid());
402 worker_process_ = worker_process.Pass();
404 // Report a handle that can be used to wait for the worker process completion,
405 // query information about the process and duplicate handles.
406 DWORD desired_access =
407 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
408 ScopedHandle limited_handle;
409 if (!DuplicateHandle(GetCurrentProcess(),
410 worker_process_,
411 GetCurrentProcess(),
412 limited_handle.Receive(),
413 desired_access,
414 FALSE,
415 0)) {
416 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
417 ReportFatalError();
418 return;
421 event_handler_->OnProcessLaunched(limited_handle.Pass());
424 } // namespace remoting