Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/win/chromoting_module.h"
      6 
      7 #include <sddl.h>
      8 
      9 #include "base/lazy_instance.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/run_loop.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/win/scoped_handle.h"
     15 #include "base/win/windows_version.h"
     16 #include "remoting/base/auto_thread_task_runner.h"
     17 #include "remoting/base/typed_buffer.h"
     18 #include "remoting/host/host_exit_codes.h"
     19 #include "remoting/host/win/com_security.h"
     20 #include "remoting/host/win/elevated_controller.h"
     21 #include "remoting/host/win/rdp_desktop_session.h"
     22 
     23 namespace remoting {
     24 
     25 namespace {
     26 
     27 // A security descriptor allowing local processes running under SYSTEM, built-in
     28 // administrators and interactive users to call COM methods.
     29 const wchar_t kElevatedControllerSd[] =
     30     SDDL_OWNER L":" SDDL_BUILTIN_ADMINISTRATORS
     31     SDDL_GROUP L":" SDDL_BUILTIN_ADMINISTRATORS
     32     SDDL_DACL L":"
     33     SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SYSTEM)
     34     SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL,
     35              SDDL_BUILTIN_ADMINISTRATORS)
     36     SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_INTERACTIVE);
     37 
     38 // Holds a reference to the task runner used by the module.
     39 base::LazyInstance<scoped_refptr<AutoThreadTaskRunner> > g_module_task_runner =
     40     LAZY_INSTANCE_INITIALIZER;
     41 
     42 // Lowers the process integrity level such that it does not exceed |max_level|.
     43 // |max_level| is expected to be one of SECURITY_MANDATORY_XXX constants.
     44 bool LowerProcessIntegrityLevel(DWORD max_level) {
     45   base::win::ScopedHandle token;
     46   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_WRITE,
     47                         token.Receive())) {
     48     PLOG(ERROR) << "OpenProcessToken() failed";
     49     return false;
     50   }
     51 
     52   TypedBuffer<TOKEN_MANDATORY_LABEL> mandatory_label;
     53   DWORD length = 0;
     54 
     55   // Get the size of the buffer needed to hold the mandatory label.
     56   BOOL result = GetTokenInformation(token, TokenIntegrityLevel,
     57                                     mandatory_label.get(), length, &length);
     58   if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
     59     // Allocate a buffer that is large enough.
     60     TypedBuffer<TOKEN_MANDATORY_LABEL> buffer(length);
     61     mandatory_label.Swap(buffer);
     62 
     63     // Get the the mandatory label.
     64     result = GetTokenInformation(token, TokenIntegrityLevel,
     65                                  mandatory_label.get(), length, &length);
     66   }
     67   if (!result) {
     68     PLOG(ERROR) << "Failed to get the mandatory label";
     69     return false;
     70   }
     71 
     72   // Read the current integrity level.
     73   DWORD sub_authority_count =
     74       *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
     75   DWORD* current_level = GetSidSubAuthority(mandatory_label->Label.Sid,
     76                                             sub_authority_count - 1);
     77 
     78   // Set the integrity level to |max_level| if needed.
     79   if (*current_level > max_level) {
     80     *current_level = max_level;
     81     if (!SetTokenInformation(token, TokenIntegrityLevel, mandatory_label.get(),
     82                              length)) {
     83       PLOG(ERROR) << "Failed to set the mandatory label";
     84       return false;
     85     }
     86   }
     87 
     88   return true;
     89 }
     90 
     91 }  // namespace
     92 
     93 ChromotingModule::ChromotingModule(
     94     ATL::_ATL_OBJMAP_ENTRY* classes,
     95     ATL::_ATL_OBJMAP_ENTRY* classes_end)
     96     : classes_(classes),
     97       classes_end_(classes_end) {
     98   // Don't do anything if COM initialization failed.
     99   if (!com_initializer_.succeeded())
    100     return;
    101 
    102   ATL::_AtlComModule.ExecuteObjectMain(true);
    103 }
    104 
    105 ChromotingModule::~ChromotingModule() {
    106   // Don't do anything if COM initialization failed.
    107   if (!com_initializer_.succeeded())
    108     return;
    109 
    110   Term();
    111   ATL::_AtlComModule.ExecuteObjectMain(false);
    112 }
    113 
    114 // static
    115 scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() {
    116   return g_module_task_runner.Get();
    117 }
    118 
    119 bool ChromotingModule::Run() {
    120   // Don't do anything if COM initialization failed.
    121   if (!com_initializer_.succeeded())
    122     return false;
    123 
    124   // Register class objects.
    125   HRESULT result = RegisterClassObjects(CLSCTX_LOCAL_SERVER,
    126                                         REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
    127   if (FAILED(result)) {
    128     LOG(ERROR) << "Failed to register class objects, result=0x"
    129                << std::hex << result << std::dec << ".";
    130     return false;
    131   }
    132 
    133   // Arrange to run |message_loop| until no components depend on it.
    134   base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
    135   base::RunLoop run_loop;
    136   g_module_task_runner.Get() = new AutoThreadTaskRunner(
    137       message_loop.message_loop_proxy(), run_loop.QuitClosure());
    138 
    139   // Start accepting activations.
    140   result = CoResumeClassObjects();
    141   if (FAILED(result)) {
    142     LOG(ERROR) << "CoResumeClassObjects() failed, result=0x"
    143                << std::hex << result << std::dec << ".";
    144     return false;
    145   }
    146 
    147   // Run the loop until the module lock counter reaches zero.
    148   run_loop.Run();
    149 
    150   // Unregister class objects.
    151   result = RevokeClassObjects();
    152   if (FAILED(result)) {
    153     LOG(ERROR) << "Failed to unregister class objects, result=0x"
    154                << std::hex << result << std::dec << ".";
    155     return false;
    156   }
    157 
    158   return true;
    159 }
    160 
    161 LONG ChromotingModule::Unlock() {
    162   LONG count = ATL::CAtlModuleT<ChromotingModule>::Unlock();
    163 
    164   if (!count) {
    165     // Stop accepting activations.
    166     HRESULT hr = CoSuspendClassObjects();
    167     CHECK(SUCCEEDED(hr));
    168 
    169     // Release the message loop reference, causing the message loop to exit.
    170     g_module_task_runner.Get() = NULL;
    171   }
    172 
    173   return count;
    174 }
    175 
    176 HRESULT ChromotingModule::RegisterClassObjects(DWORD class_context,
    177                                                DWORD flags) {
    178   for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
    179     HRESULT result = i->RegisterClassObject(class_context, flags);
    180     if (FAILED(result))
    181       return result;
    182   }
    183 
    184   return S_OK;
    185 }
    186 
    187 HRESULT ChromotingModule::RevokeClassObjects() {
    188   for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
    189     HRESULT result = i->RevokeClassObject();
    190     if (FAILED(result))
    191       return result;
    192   }
    193 
    194   return S_OK;
    195 }
    196 
    197 // Elevated controller entry point.
    198 int ElevatedControllerMain() {
    199   ATL::_ATL_OBJMAP_ENTRY elevated_controller_entry[] = {
    200     OBJECT_ENTRY(__uuidof(ElevatedController), ElevatedController)
    201   };
    202 
    203   ChromotingModule module(elevated_controller_entry,
    204                           elevated_controller_entry + 1);
    205 
    206   if (!InitializeComSecurity(WideToUTF8(kElevatedControllerSd), "", true))
    207     return kInitializationFailed;
    208 
    209   if (!module.Run())
    210     return kInitializationFailed;
    211 
    212   return kSuccessExitCode;
    213 }
    214 
    215 // RdpClient entry point.
    216 int RdpDesktopSessionMain() {
    217   // Lower the integrity level to medium, which is the lowest level at which
    218   // the RDP ActiveX control can run.
    219   if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
    220     if (!LowerProcessIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID))
    221       return kInitializationFailed;
    222   }
    223 
    224   ATL::_ATL_OBJMAP_ENTRY rdp_client_entry[] = {
    225     OBJECT_ENTRY(__uuidof(RdpDesktopSession), RdpDesktopSession)
    226   };
    227 
    228   ChromotingModule module(rdp_client_entry, rdp_client_entry + 1);
    229   return module.Run() ? kSuccessExitCode : kInitializationFailed;
    230 }
    231 
    232 } // namespace remoting
    233