Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / remoting / host / win / unprivileged_process_delegate.cc
1
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.
8
9 #include "remoting/host/win/unprivileged_process_delegate.h"
10
11 #include <sddl.h>
12
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"
34
35 using base::win::ScopedHandle;
36
37 namespace remoting {
38
39 namespace {
40
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.
48
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)";
52
53 // Mandatory label specifying low integrity level.
54 const char kLowIntegrityMandatoryLabel[] = "S:(ML;CIOI;NW;;;LW)";
55
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;;;%1$s)(A;NP;0xf037f;;;SY)(A;NP;0xf037f;;;%1$s)";
62
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)";
67
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)";
73
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", NULL, LOGON32_LOGON_SERVICE,
80                  LOGON32_PROVIDER_DEFAULT, &temp_handle)) {
81     return false;
82   }
83   ScopedHandle token(temp_handle);
84
85   sandbox::RestrictedToken restricted_token;
86   if (restricted_token.Init(token.Get()) != ERROR_SUCCESS)
87     return false;
88
89   // Remove all privileges in the token.
90   if (restricted_token.DeleteAllPrivileges(NULL) != ERROR_SUCCESS)
91     return false;
92
93   // Set low integrity level if supported by the OS.
94   if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
95     if (restricted_token.SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW)
96         != ERROR_SUCCESS) {
97       return false;
98     }
99   }
100
101   // Return the resulting token.
102   if (restricted_token.GetRestrictedTokenHandle(&temp_handle) ==
103       ERROR_SUCCESS) {
104     token_out->Set(temp_handle);
105     return true;
106   }
107   return false;
108 }
109
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     PLOG(ERROR) << "Failed to convert a SID to string";
118     return false;
119   }
120
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());
126
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;
132   }
133
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;
140   }
141
142   // GetProcessWindowStation() returns the current handle which does not need to
143   // be freed.
144   HWINSTA current_window_station = GetProcessWindowStation();
145
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()));
151
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;
157
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;
162
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;
167
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;
175   }
176
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;
181   }
182
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;
187
188   security_attributes.nLength = sizeof(security_attributes);
189   security_attributes.lpSecurityDescriptor = desktop_sd.get();
190   security_attributes.bInheritHandle = TRUE;
191
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));
197
198   // Switch back to the original window station.
199   if (!SetProcessWindowStation(current_window_station)) {
200     PLOG(ERROR) << "SetProcessWindowStation() failed";
201     return false;
202   }
203
204   if (!handles.desktop()) {
205     PLOG(ERROR) << "CreateDesktop() failed";
206     return false;
207   }
208
209   handles.Swap(*handles_out);
210   return true;
211 }
212
213 }  // namespace
214
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()) {
221 }
222
223 UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() {
224   DCHECK(CalledOnValidThread());
225   DCHECK(!channel_);
226   DCHECK(!worker_process_.IsValid());
227 }
228
229 void UnprivilegedProcessDelegate::LaunchProcess(
230     WorkerProcessLauncher* event_handler) {
231   DCHECK(CalledOnValidThread());
232   DCHECK(!event_handler_);
233
234   event_handler_ = event_handler;
235
236   scoped_ptr<IPC::ChannelProxy> server;
237
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;
244   }
245
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;
253   }
254
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;
262   }
263
264   SECURITY_ATTRIBUTES process_attributes;
265   process_attributes.nLength = sizeof(process_attributes);
266   process_attributes.lpSecurityDescriptor = process_sd.get();
267   process_attributes.bInheritHandle = FALSE;
268
269   SECURITY_ATTRIBUTES thread_attributes;
270   thread_attributes.nLength = sizeof(thread_attributes);
271   thread_attributes.lpSecurityDescriptor = thread_sd.get();
272   thread_attributes.bInheritHandle = FALSE;
273
274   ScopedHandle worker_process;
275   {
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());
279
280     // Create a connected IPC channel.
281     base::File client;
282     if (!CreateConnectedIpcChannel(io_task_runner_, this, &client, &server)) {
283       ReportFatalError();
284       return;
285     }
286
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()));
291
292     // Pass the IPC channel via the command line.
293     base::CommandLine command_line(target_command_->argv());
294     command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle);
295
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;
302     }
303
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,
313                                 0,
314                                 NULL,
315                                 &worker_process,
316                                 &worker_thread)) {
317       ReportFatalError();
318       return;
319     }
320   }
321
322   channel_ = server.Pass();
323   ReportProcessLaunched(worker_process.Pass());
324 }
325
326 void UnprivilegedProcessDelegate::Send(IPC::Message* message) {
327   DCHECK(CalledOnValidThread());
328
329   if (channel_) {
330     channel_->Send(message);
331   } else {
332     delete message;
333   }
334 }
335
336 void UnprivilegedProcessDelegate::CloseChannel() {
337   DCHECK(CalledOnValidThread());
338
339   channel_.reset();
340 }
341
342 void UnprivilegedProcessDelegate::KillProcess() {
343   DCHECK(CalledOnValidThread());
344
345   channel_.reset();
346   event_handler_ = NULL;
347
348   if (worker_process_.IsValid()) {
349     TerminateProcess(worker_process_.Get(), CONTROL_C_EXIT);
350     worker_process_.Close();
351   }
352 }
353
354 bool UnprivilegedProcessDelegate::OnMessageReceived(
355     const IPC::Message& message) {
356   DCHECK(CalledOnValidThread());
357
358   return event_handler_->OnMessageReceived(message);
359 }
360
361 void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) {
362   DCHECK(CalledOnValidThread());
363
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;
371   }
372
373   event_handler_->OnChannelConnected(peer_pid);
374 }
375
376 void UnprivilegedProcessDelegate::OnChannelError() {
377   DCHECK(CalledOnValidThread());
378
379   event_handler_->OnChannelError();
380 }
381
382 void UnprivilegedProcessDelegate::ReportFatalError() {
383   DCHECK(CalledOnValidThread());
384
385   channel_.reset();
386
387   WorkerProcessLauncher* event_handler = event_handler_;
388   event_handler_ = NULL;
389   event_handler->OnFatalError();
390 }
391
392 void UnprivilegedProcessDelegate::ReportProcessLaunched(
393     base::win::ScopedHandle worker_process) {
394   DCHECK(CalledOnValidThread());
395   DCHECK(!worker_process_.IsValid());
396
397   worker_process_ = worker_process.Pass();
398
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;
414   }
415   ScopedHandle limited_handle(temp_handle);
416
417   event_handler_->OnProcessLaunched(limited_handle.Pass());
418 }
419
420 }  // namespace remoting