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   HANDLE temp_handle;
     46   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_WRITE,
     47                         &temp_handle)) {
     48     PLOG(ERROR) << "OpenProcessToken() failed";
     49     return false;
     50   }
     51   base::win::ScopedHandle token(temp_handle);
     52 
     53   TypedBuffer<TOKEN_MANDATORY_LABEL> mandatory_label;
     54   DWORD length = 0;
     55 
     56   // Get the size of the buffer needed to hold the mandatory label.
     57   BOOL result = GetTokenInformation(token, TokenIntegrityLevel,
     58                                     mandatory_label.get(), length, &length);
     59   if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
     60     // Allocate a buffer that is large enough.
     61     TypedBuffer<TOKEN_MANDATORY_LABEL> buffer(length);
     62     mandatory_label.Swap(buffer);
     63 
     64     // Get the the mandatory label.
     65     result = GetTokenInformation(token, TokenIntegrityLevel,
     66                                  mandatory_label.get(), length, &length);
     67   }
     68   if (!result) {
     69     PLOG(ERROR) << "Failed to get the mandatory label";
     70     return false;
     71   }
     72 
     73   // Read the current integrity level.
     74   DWORD sub_authority_count =
     75       *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
     76   DWORD* current_level = GetSidSubAuthority(mandatory_label->Label.Sid,
     77                                             sub_authority_count - 1);
     78 
     79   // Set the integrity level to |max_level| if needed.
     80   if (*current_level > max_level) {
     81     *current_level = max_level;
     82     if (!SetTokenInformation(token, TokenIntegrityLevel, mandatory_label.get(),
     83                              length)) {
     84       PLOG(ERROR) << "Failed to set the mandatory label";
     85       return false;
     86     }
     87   }
     88 
     89   return true;
     90 }
     91 
     92 }  // namespace
     93 
     94 ChromotingModule::ChromotingModule(
     95     ATL::_ATL_OBJMAP_ENTRY* classes,
     96     ATL::_ATL_OBJMAP_ENTRY* classes_end)
     97     : classes_(classes),
     98       classes_end_(classes_end) {
     99   // Don't do anything if COM initialization failed.
    100   if (!com_initializer_.succeeded())
    101     return;
    102 
    103   ATL::_AtlComModule.ExecuteObjectMain(true);
    104 }
    105 
    106 ChromotingModule::~ChromotingModule() {
    107   // Don't do anything if COM initialization failed.
    108   if (!com_initializer_.succeeded())
    109     return;
    110 
    111   Term();
    112   ATL::_AtlComModule.ExecuteObjectMain(false);
    113 }
    114 
    115 // static
    116 scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() {
    117   return g_module_task_runner.Get();
    118 }
    119 
    120 bool ChromotingModule::Run() {
    121   // Don't do anything if COM initialization failed.
    122   if (!com_initializer_.succeeded())
    123     return false;
    124 
    125   // Register class objects.
    126   HRESULT result = RegisterClassObjects(CLSCTX_LOCAL_SERVER,
    127                                         REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
    128   if (FAILED(result)) {
    129     LOG(ERROR) << "Failed to register class objects, result=0x"
    130                << std::hex << result << std::dec << ".";
    131     return false;
    132   }
    133 
    134   // Arrange to run |message_loop| until no components depend on it.
    135   base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
    136   base::RunLoop run_loop;
    137   g_module_task_runner.Get() = new AutoThreadTaskRunner(
    138       message_loop.message_loop_proxy(), run_loop.QuitClosure());
    139 
    140   // Start accepting activations.
    141   result = CoResumeClassObjects();
    142   if (FAILED(result)) {
    143     LOG(ERROR) << "CoResumeClassObjects() failed, result=0x"
    144                << std::hex << result << std::dec << ".";
    145     return false;
    146   }
    147 
    148   // Run the loop until the module lock counter reaches zero.
    149   run_loop.Run();
    150 
    151   // Unregister class objects.
    152   result = RevokeClassObjects();
    153   if (FAILED(result)) {
    154     LOG(ERROR) << "Failed to unregister class objects, result=0x"
    155                << std::hex << result << std::dec << ".";
    156     return false;
    157   }
    158 
    159   return true;
    160 }
    161 
    162 LONG ChromotingModule::Unlock() {
    163   LONG count = ATL::CAtlModuleT<ChromotingModule>::Unlock();
    164 
    165   if (!count) {
    166     // Stop accepting activations.
    167     HRESULT hr = CoSuspendClassObjects();
    168     CHECK(SUCCEEDED(hr));
    169 
    170     // Release the message loop reference, causing the message loop to exit.
    171     g_module_task_runner.Get() = NULL;
    172   }
    173 
    174   return count;
    175 }
    176 
    177 HRESULT ChromotingModule::RegisterClassObjects(DWORD class_context,
    178                                                DWORD flags) {
    179   for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
    180     HRESULT result = i->RegisterClassObject(class_context, flags);
    181     if (FAILED(result))
    182       return result;
    183   }
    184 
    185   return S_OK;
    186 }
    187 
    188 HRESULT ChromotingModule::RevokeClassObjects() {
    189   for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
    190     HRESULT result = i->RevokeClassObject();
    191     if (FAILED(result))
    192       return result;
    193   }
    194 
    195   return S_OK;
    196 }
    197 
    198 // Elevated controller entry point.
    199 int ElevatedControllerMain() {
    200   ATL::_ATL_OBJMAP_ENTRY elevated_controller_entry[] = {
    201     OBJECT_ENTRY(__uuidof(ElevatedController), ElevatedController)
    202   };
    203 
    204   ChromotingModule module(elevated_controller_entry,
    205                           elevated_controller_entry + 1);
    206 
    207   if (!InitializeComSecurity(WideToUTF8(kElevatedControllerSd), "", true))
    208     return kInitializationFailed;
    209 
    210   if (!module.Run())
    211     return kInitializationFailed;
    212 
    213   return kSuccessExitCode;
    214 }
    215 
    216 // RdpClient entry point.
    217 int RdpDesktopSessionMain() {
    218   // Lower the integrity level to medium, which is the lowest level at which
    219   // the RDP ActiveX control can run.
    220   if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
    221     if (!LowerProcessIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID))
    222       return kInitializationFailed;
    223   }
    224 
    225   ATL::_ATL_OBJMAP_ENTRY rdp_client_entry[] = {
    226     OBJECT_ENTRY(__uuidof(RdpDesktopSession), RdpDesktopSession)
    227   };
    228 
    229   ChromotingModule module(rdp_client_entry, rdp_client_entry + 1);
    230   return module.Run() ? kSuccessExitCode : kInitializationFailed;
    231 }
    232 
    233 } // namespace remoting
    234