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/logging.h" 15 #include "base/rand_util.h" 16 #include "base/single_thread_task_runner.h" 17 #include "base/strings/string16.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/synchronization/lock.h" 21 #include "base/win/scoped_handle.h" 22 #include "base/win/windows_version.h" 23 #include "ipc/ipc_channel.h" 24 #include "ipc/ipc_channel_proxy.h" 25 #include "ipc/ipc_message.h" 26 #include "remoting/base/typed_buffer.h" 27 #include "remoting/host/ipc_constants.h" 28 #include "remoting/host/ipc_util.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" 33 34 using base::win::ScopedHandle; 35 36 namespace remoting { 37 38 namespace { 39 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. 47 48 // Security descriptor of the desktop the worker process attaches to. It gives 49 // SYSTEM and the logon SID full access to the desktop. 50 const char kDesktopSdFormat[] = "O:SYG:SYD:(A;;0xf01ff;;;SY)(A;;0xf01ff;;;%s)"; 51 52 // Mandatory label specifying low integrity level. 53 const char kLowIntegrityMandatoryLabel[] = "S:(ML;CIOI;NW;;;LW)"; 54 55 // Security descriptor of the window station the worker process attaches to. It 56 // gives SYSTEM and the logon SID full access the window station. The child 57 // containers and objects inherit ACE giving SYSTEM and the logon SID full 58 // access to them as well. 59 const char kWindowStationSdFormat[] = "O:SYG:SYD:(A;CIOIIO;GA;;;SY)" 60 "(A;CIOIIO;GA;;;%1$s)(A;NP;0xf037f;;;SY)(A;NP;0xf037f;;;%1$s)"; 61 62 // Security descriptor of the worker process. It gives access SYSTEM full access 63 // to the process. It gives READ_CONTROL, SYNCHRONIZE, PROCESS_QUERY_INFORMATION 64 // and PROCESS_TERMINATE rights to the built-in administrators group. 65 const char kWorkerProcessSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120401;;;BA)"; 66 67 // Security descriptor of the worker process threads. It gives access SYSTEM 68 // full access to the threads. It gives READ_CONTROL, SYNCHRONIZE, 69 // THREAD_QUERY_INFORMATION and THREAD_TERMINATE rights to the built-in 70 // administrators group. 71 const char kWorkerThreadSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120801;;;BA)"; 72 73 // Creates a token with limited access that will be used to run the worker 74 // process. 75 bool CreateRestrictedToken(ScopedHandle* token_out) { 76 // Create a token representing LocalService account. 77 ScopedHandle token; 78 if (!LogonUser(L"LocalService", L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, 79 LOGON32_PROVIDER_DEFAULT, token.Receive())) { 80 return false; 81 } 82 83 sandbox::RestrictedToken restricted_token; 84 if (restricted_token.Init(token) != ERROR_SUCCESS) 85 return false; 86 87 // Remove all privileges in the token. 88 if (restricted_token.DeleteAllPrivileges(NULL) != ERROR_SUCCESS) 89 return false; 90 91 // Set low integrity level if supported by the OS. 92 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 93 if (restricted_token.SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW) 94 != ERROR_SUCCESS) { 95 return false; 96 } 97 } 98 99 // Return the resulting token. 100 return restricted_token.GetRestrictedTokenHandle(token_out->Receive()) == 101 ERROR_SUCCESS; 102 } 103 104 // Creates a window station with a given name and the default desktop giving 105 // the complete access to |logon_sid|. 106 bool CreateWindowStationAndDesktop(ScopedSid logon_sid, 107 WindowStationAndDesktop* handles_out) { 108 // Convert the logon SID into a string. 109 std::string logon_sid_string = ConvertSidToString(logon_sid.get()); 110 if (logon_sid_string.empty()) { 111 LOG_GETLASTERROR(ERROR) << "Failed to convert a SID to string"; 112 return false; 113 } 114 115 // Format the security descriptors in SDDL form. 116 std::string desktop_sddl = 117 base::StringPrintf(kDesktopSdFormat, logon_sid_string.c_str()); 118 std::string window_station_sddl = 119 base::StringPrintf(kWindowStationSdFormat, logon_sid_string.c_str()); 120 121 // The worker runs at low integrity level. Make sure it will be able to attach 122 // to the window station and desktop. 123 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 124 desktop_sddl += kLowIntegrityMandatoryLabel; 125 window_station_sddl += kLowIntegrityMandatoryLabel; 126 } 127 128 // Create the desktop and window station security descriptors. 129 ScopedSd desktop_sd = ConvertSddlToSd(desktop_sddl); 130 ScopedSd window_station_sd = ConvertSddlToSd(window_station_sddl); 131 if (!desktop_sd || !window_station_sd) { 132 LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor."; 133 return false; 134 } 135 136 // GetProcessWindowStation() returns the current handle which does not need to 137 // be freed. 138 HWINSTA current_window_station = GetProcessWindowStation(); 139 140 // Generate a unique window station name. 141 std::string window_station_name = base::StringPrintf( 142 "chromoting-%d-%d", 143 base::GetCurrentProcId(), 144 base::RandInt(1, std::numeric_limits<int>::max())); 145 146 // Make sure that a new window station will be created instead of opening 147 // an existing one. 148 DWORD window_station_flags = 0; 149 if (base::win::GetVersion() >= base::win::VERSION_VISTA) 150 window_station_flags = CWF_CREATE_ONLY; 151 152 // Request full access because this handle will be inherited by the worker 153 // process which needs full access in order to attach to the window station. 154 DWORD desired_access = 155 WINSTA_ALL_ACCESS | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER; 156 157 SECURITY_ATTRIBUTES security_attributes = {0}; 158 security_attributes.nLength = sizeof(security_attributes); 159 security_attributes.lpSecurityDescriptor = window_station_sd.get(); 160 security_attributes.bInheritHandle = TRUE; 161 162 WindowStationAndDesktop handles; 163 handles.SetWindowStation(CreateWindowStation( 164 UTF8ToUTF16(window_station_name).c_str(), window_station_flags, 165 desired_access, &security_attributes)); 166 if (!handles.window_station()) { 167 LOG_GETLASTERROR(ERROR) << "CreateWindowStation() failed"; 168 return false; 169 } 170 171 // Switch to the new window station and create a desktop on it. 172 if (!SetProcessWindowStation(handles.window_station())) { 173 LOG_GETLASTERROR(ERROR) << "SetProcessWindowStation() failed"; 174 return false; 175 } 176 177 desired_access = DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | 178 DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | 179 DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | 180 DESKTOP_SWITCHDESKTOP | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER; 181 182 security_attributes.nLength = sizeof(security_attributes); 183 security_attributes.lpSecurityDescriptor = desktop_sd.get(); 184 security_attributes.bInheritHandle = TRUE; 185 186 // The default desktop of the interactive window station is called "Default". 187 // Name the created desktop the same way in case any code relies on that. 188 // The desktop name should not make any difference though. 189 handles.SetDesktop(CreateDesktop(L"Default", NULL, NULL, 0, desired_access, 190 &security_attributes)); 191 192 // Switch back to the original window station. 193 if (!SetProcessWindowStation(current_window_station)) { 194 LOG_GETLASTERROR(ERROR) << "SetProcessWindowStation() failed"; 195 return false; 196 } 197 198 if (!handles.desktop()) { 199 LOG_GETLASTERROR(ERROR) << "CreateDesktop() failed"; 200 return false; 201 } 202 203 handles.Swap(*handles_out); 204 return true; 205 } 206 207 } // namespace 208 209 UnprivilegedProcessDelegate::UnprivilegedProcessDelegate( 210 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 211 scoped_ptr<CommandLine> target_command) 212 : io_task_runner_(io_task_runner), 213 event_handler_(NULL), 214 target_command_(target_command.Pass()) { 215 } 216 217 UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() { 218 DCHECK(CalledOnValidThread()); 219 DCHECK(!channel_); 220 DCHECK(!worker_process_.IsValid()); 221 } 222 223 void UnprivilegedProcessDelegate::LaunchProcess( 224 WorkerProcessLauncher* event_handler) { 225 DCHECK(CalledOnValidThread()); 226 DCHECK(!event_handler_); 227 228 event_handler_ = event_handler; 229 230 scoped_ptr<IPC::ChannelProxy> server; 231 232 // Create a restricted token that will be used to run the worker process. 233 ScopedHandle token; 234 if (!CreateRestrictedToken(&token)) { 235 LOG_GETLASTERROR(ERROR) 236 << "Failed to create a restricted LocalService token"; 237 ReportFatalError(); 238 return; 239 } 240 241 // Determine our logon SID, so we can grant it access to our window station 242 // and desktop. 243 ScopedSid logon_sid = GetLogonSid(token); 244 if (!logon_sid) { 245 LOG_GETLASTERROR(ERROR) << "Failed to retrieve the logon SID"; 246 ReportFatalError(); 247 return; 248 } 249 250 // Create the process and thread security descriptors. 251 ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd); 252 ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd); 253 if (!process_sd || !thread_sd) { 254 LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor"; 255 ReportFatalError(); 256 return; 257 } 258 259 SECURITY_ATTRIBUTES process_attributes; 260 process_attributes.nLength = sizeof(process_attributes); 261 process_attributes.lpSecurityDescriptor = process_sd.get(); 262 process_attributes.bInheritHandle = FALSE; 263 264 SECURITY_ATTRIBUTES thread_attributes; 265 thread_attributes.nLength = sizeof(thread_attributes); 266 thread_attributes.lpSecurityDescriptor = thread_sd.get(); 267 thread_attributes.bInheritHandle = FALSE; 268 269 ScopedHandle worker_process; 270 { 271 // Take a lock why any inheritable handles are open to make sure that only 272 // one process inherits them. 273 base::AutoLock lock(g_inherit_handles_lock.Get()); 274 275 // Create a connected IPC channel. 276 ScopedHandle client; 277 if (!CreateConnectedIpcChannel(io_task_runner_, this, client.Receive(), 278 &server)) { 279 ReportFatalError(); 280 return; 281 } 282 283 // Convert the handle value into a decimal integer. Handle values are 32bit 284 // even on 64bit platforms. 285 std::string pipe_handle = base::StringPrintf( 286 "%d", reinterpret_cast<ULONG_PTR>(client.Get())); 287 288 // Pass the IPC channel via the command line. 289 CommandLine command_line(target_command_->argv()); 290 command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle); 291 292 // Create our own window station and desktop accessible by |logon_sid|. 293 WindowStationAndDesktop handles; 294 if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) { 295 LOG_GETLASTERROR(ERROR) 296 << "Failed to create a window station and desktop"; 297 ReportFatalError(); 298 return; 299 } 300 301 // Try to launch the worker process. The launched process inherits 302 // the window station, desktop and pipe handles, created above. 303 ScopedHandle worker_thread; 304 if (!LaunchProcessWithToken(command_line.GetProgram(), 305 command_line.GetCommandLineString(), 306 token, 307 &process_attributes, 308 &thread_attributes, 309 true, 310 0, 311 NULL, 312 &worker_process, 313 &worker_thread)) { 314 ReportFatalError(); 315 return; 316 } 317 } 318 319 channel_ = server.Pass(); 320 ReportProcessLaunched(worker_process.Pass()); 321 } 322 323 void UnprivilegedProcessDelegate::Send(IPC::Message* message) { 324 DCHECK(CalledOnValidThread()); 325 326 if (channel_) { 327 channel_->Send(message); 328 } else { 329 delete message; 330 } 331 } 332 333 void UnprivilegedProcessDelegate::CloseChannel() { 334 DCHECK(CalledOnValidThread()); 335 336 channel_.reset(); 337 } 338 339 void UnprivilegedProcessDelegate::KillProcess() { 340 DCHECK(CalledOnValidThread()); 341 342 channel_.reset(); 343 event_handler_ = NULL; 344 345 if (worker_process_.IsValid()) { 346 TerminateProcess(worker_process_, CONTROL_C_EXIT); 347 worker_process_.Close(); 348 } 349 } 350 351 bool UnprivilegedProcessDelegate::OnMessageReceived( 352 const IPC::Message& message) { 353 DCHECK(CalledOnValidThread()); 354 355 return event_handler_->OnMessageReceived(message); 356 } 357 358 void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) { 359 DCHECK(CalledOnValidThread()); 360 361 DWORD pid = GetProcessId(worker_process_); 362 if (pid != static_cast<DWORD>(peer_pid)) { 363 LOG(ERROR) << "The actual client PID " << pid 364 << " does not match the one reported by the client: " 365 << peer_pid; 366 ReportFatalError(); 367 return; 368 } 369 370 event_handler_->OnChannelConnected(peer_pid); 371 } 372 373 void UnprivilegedProcessDelegate::OnChannelError() { 374 DCHECK(CalledOnValidThread()); 375 376 event_handler_->OnChannelError(); 377 } 378 379 void UnprivilegedProcessDelegate::ReportFatalError() { 380 DCHECK(CalledOnValidThread()); 381 382 channel_.reset(); 383 384 WorkerProcessLauncher* event_handler = event_handler_; 385 event_handler_ = NULL; 386 event_handler->OnFatalError(); 387 } 388 389 void UnprivilegedProcessDelegate::ReportProcessLaunched( 390 base::win::ScopedHandle worker_process) { 391 DCHECK(CalledOnValidThread()); 392 DCHECK(!worker_process_.IsValid()); 393 394 worker_process_ = worker_process.Pass(); 395 396 // Report a handle that can be used to wait for the worker process completion, 397 // query information about the process and duplicate handles. 398 DWORD desired_access = 399 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; 400 ScopedHandle limited_handle; 401 if (!DuplicateHandle(GetCurrentProcess(), 402 worker_process_, 403 GetCurrentProcess(), 404 limited_handle.Receive(), 405 desired_access, 406 FALSE, 407 0)) { 408 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; 409 ReportFatalError(); 410 return; 411 } 412 413 event_handler_->OnProcessLaunched(limited_handle.Pass()); 414 } 415 416 } // namespace remoting 417