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.
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"
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
;
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
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
81 bool CreateRestrictedToken(ScopedHandle
* token_out
) {
82 // Create a token representing LocalService account.
84 if (!LogonUser(L
"LocalService", L
"NT AUTHORITY", NULL
, LOGON32_LOGON_SERVICE
,
85 LOGON32_PROVIDER_DEFAULT
, token
.Receive())) {
89 sandbox::RestrictedToken restricted_token
;
90 if (restricted_token
.Init(token
) != ERROR_SUCCESS
)
93 // Remove all privileges in the token.
94 if (restricted_token
.DeleteAllPrivileges(NULL
) != ERROR_SUCCESS
)
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
)
105 // Return the resulting token.
106 return restricted_token
.GetRestrictedTokenHandle(token_out
->Receive()) ==
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";
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.";
142 // GetProcessWindowStation() returns the current handle which does not need to
144 HWINSTA current_window_station
= GetProcessWindowStation();
146 // Generate a unique window station name.
147 std::string window_station_name
= base::StringPrintf(
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
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";
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";
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";
204 if (!handles
.desktop()) {
205 LOG_GETLASTERROR(ERROR
) << "CreateDesktop() failed";
209 handles
.Swap(*handles_out
);
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());
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.
243 if (!CreateRestrictedToken(&token
)) {
244 LOG_GETLASTERROR(ERROR
)
245 << "Failed to create a restricted LocalService token";
250 // Determine our logon SID, so we can grant it access to our window station
252 ScopedSid logon_sid
= GetLogonSid(token
);
254 LOG_GETLASTERROR(ERROR
) << "Failed to retrieve the logon SID";
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";
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.
286 if (!CreateConnectedIpcChannel(channel_name
, kDaemonIpcSd
, io_task_runner_
,
287 this, &client
, &server
)) {
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";
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(),
328 channel_
= server
.Pass();
329 ReportProcessLaunched(worker_process
.Pass());
332 void UnprivilegedProcessDelegate::Send(IPC::Message
* message
) {
333 DCHECK(CalledOnValidThread());
336 channel_
->Send(message
);
342 void UnprivilegedProcessDelegate::CloseChannel() {
343 DCHECK(CalledOnValidThread());
348 void UnprivilegedProcessDelegate::KillProcess() {
349 DCHECK(CalledOnValidThread());
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: "
378 event_handler_
->OnChannelConnected(peer_pid
);
381 void UnprivilegedProcessDelegate::OnChannelError() {
382 DCHECK(CalledOnValidThread());
384 event_handler_
->OnChannelError();
387 void UnprivilegedProcessDelegate::ReportFatalError() {
388 DCHECK(CalledOnValidThread());
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(),
412 limited_handle
.Receive(),
416 LOG_GETLASTERROR(ERROR
) << "Failed to duplicate a handle";
421 event_handler_
->OnProcessLaunched(limited_handle
.Pass());
424 } // namespace remoting