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_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
;
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
.GetRestrictedToken(token_out
) == ERROR_SUCCESS
)
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";
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.";
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 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";
177 // Switch to the new window station and create a desktop on it.
178 if (!SetProcessWindowStation(handles
.window_station())) {
179 PLOG(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", 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";
204 if (!handles
.desktop()) {
205 PLOG(ERROR
) << "CreateDesktop() failed";
209 handles
.Swap(*handles_out
);
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());
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.
240 if (!CreateRestrictedToken(&token
)) {
241 PLOG(ERROR
) << "Failed to create a restricted LocalService token";
246 // Determine our logon SID, so we can grant it access to our window station
248 ScopedSid logon_sid
= GetLogonSid(token
.Get());
250 PLOG(ERROR
) << "Failed to retrieve the logon SID";
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";
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.
282 if (!CreateConnectedIpcChannel(io_task_runner_
, this, &client
, &server
)) {
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";
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(),
322 channel_
= server
.Pass();
323 ReportProcessLaunched(worker_process
.Pass());
326 void UnprivilegedProcessDelegate::Send(IPC::Message
* message
) {
327 DCHECK(CalledOnValidThread());
330 channel_
->Send(message
);
336 void UnprivilegedProcessDelegate::CloseChannel() {
337 DCHECK(CalledOnValidThread());
342 void UnprivilegedProcessDelegate::KillProcess() {
343 DCHECK(CalledOnValidThread());
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: "
373 event_handler_
->OnChannelConnected(peer_pid
);
376 void UnprivilegedProcessDelegate::OnChannelError() {
377 DCHECK(CalledOnValidThread());
379 event_handler_
->OnChannelError();
382 void UnprivilegedProcessDelegate::ReportFatalError() {
383 DCHECK(CalledOnValidThread());
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
;
404 if (!DuplicateHandle(GetCurrentProcess(),
405 worker_process_
.Get(),
411 PLOG(ERROR
) << "Failed to duplicate a handle";
415 ScopedHandle
limited_handle(temp_handle
);
417 event_handler_
->OnProcessLaunched(limited_handle
.Pass());
420 } // namespace remoting