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/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
;
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
76 bool CreateRestrictedToken(ScopedHandle
* token_out
) {
77 // Create a token representing LocalService account.
79 if (!LogonUser(L
"LocalService", L
"NT AUTHORITY", nullptr,
80 LOGON32_LOGON_SERVICE
, LOGON32_PROVIDER_DEFAULT
,
84 ScopedHandle
token(temp_handle
);
86 sandbox::RestrictedToken restricted_token
;
87 if (restricted_token
.Init(token
.Get()) != ERROR_SUCCESS
)
90 // Remove all privileges in the token.
91 if (restricted_token
.DeleteAllPrivileges(nullptr) != ERROR_SUCCESS
)
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
)
102 // Return the resulting token.
103 if (restricted_token
.GetRestrictedTokenHandle(&temp_handle
) ==
105 token_out
->Set(temp_handle
);
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";
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.";
144 // GetProcessWindowStation() returns the current handle which does not need to
146 HWINSTA current_window_station
= GetProcessWindowStation();
148 // Generate a unique window station name.
149 std::string window_station_name
= base::StringPrintf(
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
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";
179 // Switch to the new window station and create a desktop on it.
180 if (!SetProcessWindowStation(handles
.window_station())) {
181 PLOG(ERROR
) << "SetProcessWindowStation() failed";
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";
206 if (!handles
.desktop()) {
207 PLOG(ERROR
) << "CreateDesktop() failed";
211 handles
.Swap(*handles_out
);
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());
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.
242 if (!CreateRestrictedToken(&token
)) {
243 PLOG(ERROR
) << "Failed to create a restricted LocalService token";
248 // Determine our logon SID, so we can grant it access to our window station
250 ScopedSid logon_sid
= GetLogonSid(token
.Get());
252 PLOG(ERROR
) << "Failed to retrieve the logon SID";
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";
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.
284 if (!CreateConnectedIpcChannel(io_task_runner_
, this, &client
, &server
)) {
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";
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(),
324 channel_
= server
.Pass();
325 ReportProcessLaunched(worker_process
.Pass());
328 void UnprivilegedProcessDelegate::Send(IPC::Message
* message
) {
329 DCHECK(CalledOnValidThread());
332 channel_
->Send(message
);
338 void UnprivilegedProcessDelegate::CloseChannel() {
339 DCHECK(CalledOnValidThread());
344 void UnprivilegedProcessDelegate::KillProcess() {
345 DCHECK(CalledOnValidThread());
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: "
375 event_handler_
->OnChannelConnected(peer_pid
);
378 void UnprivilegedProcessDelegate::OnChannelError() {
379 DCHECK(CalledOnValidThread());
381 event_handler_
->OnChannelError();
384 void UnprivilegedProcessDelegate::ReportFatalError() {
385 DCHECK(CalledOnValidThread());
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
;
406 if (!DuplicateHandle(GetCurrentProcess(),
407 worker_process_
.Get(),
413 PLOG(ERROR
) << "Failed to duplicate a handle";
417 ScopedHandle
limited_handle(temp_handle
);
419 event_handler_
->OnProcessLaunched(limited_handle
.Pass());
422 } // namespace remoting