Home | History | Annotate | Download | only in win
      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