root/remoting/host/win/chromoting_module.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. LowerProcessIntegrityLevel
  2. classes_end_
  3. task_runner
  4. Run
  5. Unlock
  6. RegisterClassObjects
  7. RevokeClassObjects
  8. ElevatedControllerMain
  9. RdpDesktopSessionMain

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/win/chromoting_module.h"

#include <sddl.h>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/typed_buffer.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/win/com_security.h"
#include "remoting/host/win/elevated_controller.h"
#include "remoting/host/win/rdp_desktop_session.h"

namespace remoting {

namespace {

// A security descriptor allowing local processes running under SYSTEM, built-in
// administrators and interactive users to call COM methods.
const wchar_t kElevatedControllerSd[] =
    SDDL_OWNER L":" SDDL_BUILTIN_ADMINISTRATORS
    SDDL_GROUP L":" SDDL_BUILTIN_ADMINISTRATORS
    SDDL_DACL L":"
    SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SYSTEM)
    SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL,
             SDDL_BUILTIN_ADMINISTRATORS)
    SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_INTERACTIVE);

// Holds a reference to the task runner used by the module.
base::LazyInstance<scoped_refptr<AutoThreadTaskRunner> > g_module_task_runner =
    LAZY_INSTANCE_INITIALIZER;

// Lowers the process integrity level such that it does not exceed |max_level|.
// |max_level| is expected to be one of SECURITY_MANDATORY_XXX constants.
bool LowerProcessIntegrityLevel(DWORD max_level) {
  HANDLE temp_handle;
  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_WRITE,
                        &temp_handle)) {
    PLOG(ERROR) << "OpenProcessToken() failed";
    return false;
  }
  base::win::ScopedHandle token(temp_handle);

  TypedBuffer<TOKEN_MANDATORY_LABEL> mandatory_label;
  DWORD length = 0;

  // Get the size of the buffer needed to hold the mandatory label.
  BOOL result = GetTokenInformation(token, TokenIntegrityLevel,
                                    mandatory_label.get(), length, &length);
  if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    // Allocate a buffer that is large enough.
    TypedBuffer<TOKEN_MANDATORY_LABEL> buffer(length);
    mandatory_label.Swap(buffer);

    // Get the the mandatory label.
    result = GetTokenInformation(token, TokenIntegrityLevel,
                                 mandatory_label.get(), length, &length);
  }
  if (!result) {
    PLOG(ERROR) << "Failed to get the mandatory label";
    return false;
  }

  // Read the current integrity level.
  DWORD sub_authority_count =
      *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
  DWORD* current_level = GetSidSubAuthority(mandatory_label->Label.Sid,
                                            sub_authority_count - 1);

  // Set the integrity level to |max_level| if needed.
  if (*current_level > max_level) {
    *current_level = max_level;
    if (!SetTokenInformation(token, TokenIntegrityLevel, mandatory_label.get(),
                             length)) {
      PLOG(ERROR) << "Failed to set the mandatory label";
      return false;
    }
  }

  return true;
}

}  // namespace

ChromotingModule::ChromotingModule(
    ATL::_ATL_OBJMAP_ENTRY* classes,
    ATL::_ATL_OBJMAP_ENTRY* classes_end)
    : classes_(classes),
      classes_end_(classes_end) {
  // Don't do anything if COM initialization failed.
  if (!com_initializer_.succeeded())
    return;

  ATL::_AtlComModule.ExecuteObjectMain(true);
}

ChromotingModule::~ChromotingModule() {
  // Don't do anything if COM initialization failed.
  if (!com_initializer_.succeeded())
    return;

  Term();
  ATL::_AtlComModule.ExecuteObjectMain(false);
}

// static
scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() {
  return g_module_task_runner.Get();
}

bool ChromotingModule::Run() {
  // Don't do anything if COM initialization failed.
  if (!com_initializer_.succeeded())
    return false;

  // Register class objects.
  HRESULT result = RegisterClassObjects(CLSCTX_LOCAL_SERVER,
                                        REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
  if (FAILED(result)) {
    LOG(ERROR) << "Failed to register class objects, result=0x"
               << std::hex << result << std::dec << ".";
    return false;
  }

  // Arrange to run |message_loop| until no components depend on it.
  base::MessageLoopForUI message_loop;
  base::RunLoop run_loop;
  g_module_task_runner.Get() = new AutoThreadTaskRunner(
      message_loop.message_loop_proxy(), run_loop.QuitClosure());

  // Start accepting activations.
  result = CoResumeClassObjects();
  if (FAILED(result)) {
    LOG(ERROR) << "CoResumeClassObjects() failed, result=0x"
               << std::hex << result << std::dec << ".";
    return false;
  }

  // Run the loop until the module lock counter reaches zero.
  run_loop.Run();

  // Unregister class objects.
  result = RevokeClassObjects();
  if (FAILED(result)) {
    LOG(ERROR) << "Failed to unregister class objects, result=0x"
               << std::hex << result << std::dec << ".";
    return false;
  }

  return true;
}

LONG ChromotingModule::Unlock() {
  LONG count = ATL::CAtlModuleT<ChromotingModule>::Unlock();

  if (!count) {
    // Stop accepting activations.
    HRESULT hr = CoSuspendClassObjects();
    CHECK(SUCCEEDED(hr));

    // Release the message loop reference, causing the message loop to exit.
    g_module_task_runner.Get() = NULL;
  }

  return count;
}

HRESULT ChromotingModule::RegisterClassObjects(DWORD class_context,
                                               DWORD flags) {
  for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
    HRESULT result = i->RegisterClassObject(class_context, flags);
    if (FAILED(result))
      return result;
  }

  return S_OK;
}

HRESULT ChromotingModule::RevokeClassObjects() {
  for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
    HRESULT result = i->RevokeClassObject();
    if (FAILED(result))
      return result;
  }

  return S_OK;
}

// Elevated controller entry point.
int ElevatedControllerMain() {
  ATL::_ATL_OBJMAP_ENTRY elevated_controller_entry[] = {
    OBJECT_ENTRY(__uuidof(ElevatedController), ElevatedController)
  };

  ChromotingModule module(elevated_controller_entry,
                          elevated_controller_entry + 1);

  if (!InitializeComSecurity(base::WideToUTF8(kElevatedControllerSd), "", true))
    return kInitializationFailed;

  if (!module.Run())
    return kInitializationFailed;

  return kSuccessExitCode;
}

// RdpClient entry point.
int RdpDesktopSessionMain() {
  // Lower the integrity level to medium, which is the lowest level at which
  // the RDP ActiveX control can run.
  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
    if (!LowerProcessIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID))
      return kInitializationFailed;
  }

  ATL::_ATL_OBJMAP_ENTRY rdp_client_entry[] = {
    OBJECT_ENTRY(__uuidof(RdpDesktopSession), RdpDesktopSession)
  };

  ChromotingModule module(rdp_client_entry, rdp_client_entry + 1);
  return module.Run() ? kSuccessExitCode : kInitializationFailed;
}

} // namespace remoting

/* [<][>][^][v][top][bottom][index][help] */