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 35using base::win::ScopedHandle; 36 37namespace remoting { 38 39namespace { 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. 51const char kDesktopSdFormat[] = "O:SYG:SYD:(A;;0xf01ff;;;SY)(A;;0xf01ff;;;%s)"; 52 53// Mandatory label specifying low integrity level. 54const 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. 60const 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. 66const 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. 72const 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. 76bool 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|. 112bool 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 215UnprivilegedProcessDelegate::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 223UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() { 224 DCHECK(CalledOnValidThread()); 225 DCHECK(!channel_); 226 DCHECK(!worker_process_.IsValid()); 227} 228 229void 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 326void UnprivilegedProcessDelegate::Send(IPC::Message* message) { 327 DCHECK(CalledOnValidThread()); 328 329 if (channel_) { 330 channel_->Send(message); 331 } else { 332 delete message; 333 } 334} 335 336void UnprivilegedProcessDelegate::CloseChannel() { 337 DCHECK(CalledOnValidThread()); 338 339 channel_.reset(); 340} 341 342void 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 354bool UnprivilegedProcessDelegate::OnMessageReceived( 355 const IPC::Message& message) { 356 DCHECK(CalledOnValidThread()); 357 358 return event_handler_->OnMessageReceived(message); 359} 360 361void 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 376void UnprivilegedProcessDelegate::OnChannelError() { 377 DCHECK(CalledOnValidThread()); 378 379 event_handler_->OnChannelError(); 380} 381 382void 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 392void 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 421