This source file includes following definitions.
- IsClientAdmin
- ReadConfig
- GetTempLocationFor
- WriteConfigFileToTemp
- MoveConfigFileFromTemp
- WriteConfig
- FinalConstruct
- FinalRelease
- GetConfig
- GetVersion
- SetConfig
- SetOwnerWindow
- StartDaemon
- StopDaemon
- UpdateConfig
- GetUsageStatsConsent
- SetUsageStatsConsent
- OpenService
#include "remoting/host/win/elevated_controller.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/process/memory.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/scoped_handle.h"
#include "remoting/host/branding.h"
#include "remoting/host/usage_stats_consent.h"
#include "remoting/host/verify_config_window_win.h"
#include "remoting/host/win/core_resource.h"
#include "remoting/host/win/security_descriptor.h"
namespace remoting {
namespace {
const size_t kMaxConfigFileSize = 1024 * 1024;
const base::FilePath::CharType kConfigFileName[] = FILE_PATH_LITERAL("host.json");
const base::FilePath::CharType kUnprivilegedConfigFileName[] =
    FILE_PATH_LITERAL("host_unprivileged.json");
const base::FilePath::CharType kTempFileExtension[] = FILE_PATH_LITERAL("json~");
const char kConfigFileSecurityDescriptor[] =
    "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
const char kUnprivilegedConfigFileSecurityDescriptor[] =
    "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)";
const char kHostId[] = "host_id";
const char kXmppLogin[] = "xmpp_login";
const char kHostOwner[] = "host_owner";
const char kHostSecretHash[] = "host_secret_hash";
const char* const kReadonlyKeys[] = { kHostId, kHostOwner, kXmppLogin };
const char* const kUnprivilegedConfigKeys[] = { kHostId, kXmppLogin };
bool IsClientAdmin() {
  HRESULT hr = CoImpersonateClient();
  if (FAILED(hr)) {
    return false;
  }
  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
  PSID administrators_group = NULL;
  BOOL result = AllocateAndInitializeSid(&nt_authority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0, 0, 0, 0, 0, 0,
                                         &administrators_group);
  if (result) {
    if (!CheckTokenMembership(NULL, administrators_group, &result)) {
      result = false;
    }
    FreeSid(administrators_group);
  }
  hr = CoRevertToSelf();
  CHECK(SUCCEEDED(hr));
  return !!result;
}
HRESULT ReadConfig(const base::FilePath& filename,
                   scoped_ptr<base::DictionaryValue>* config_out) {
  
  base::win::ScopedHandle file(
      CreateFileW(filename.value().c_str(),
                  GENERIC_READ,
                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                  NULL,
                  OPEN_EXISTING,
                  FILE_FLAG_SEQUENTIAL_SCAN,
                  NULL));
  if (!file.IsValid()) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to open '" << filename.value() << "'";
    return HRESULT_FROM_WIN32(error);
  }
  scoped_ptr<char[]> buffer(new char[kMaxConfigFileSize]);
  DWORD size = kMaxConfigFileSize;
  if (!::ReadFile(file, &buffer[0], size, &size, NULL)) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to read '" << filename.value() << "'";
    return HRESULT_FROM_WIN32(error);
  }
  
  std::string file_content(buffer.get(), size);
  scoped_ptr<base::Value> value(
      base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS));
  base::DictionaryValue* dictionary;
  if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) {
    LOG(ERROR) << "Failed to read '" << filename.value() << "'.";
    return E_FAIL;
  }
  value.release();
  config_out->reset(dictionary);
  return S_OK;
}
base::FilePath GetTempLocationFor(const base::FilePath& filename) {
  return filename.ReplaceExtension(kTempFileExtension);
}
HRESULT WriteConfigFileToTemp(const base::FilePath& filename,
                              const char* security_descriptor,
                              const char* content,
                              size_t length) {
  
  ScopedSd sd = ConvertSddlToSd(security_descriptor);
  if (!sd) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR) <<
        "Failed to create a security descriptor for the configuration file";
    return HRESULT_FROM_WIN32(error);
  }
  SECURITY_ATTRIBUTES security_attributes = {0};
  security_attributes.nLength = sizeof(security_attributes);
  security_attributes.lpSecurityDescriptor = sd.get();
  security_attributes.bInheritHandle = FALSE;
  
  base::FilePath tempname = GetTempLocationFor(filename);
  base::win::ScopedHandle file(
      CreateFileW(tempname.value().c_str(),
                  GENERIC_WRITE,
                  0,
                  &security_attributes,
                  CREATE_ALWAYS,
                  FILE_FLAG_SEQUENTIAL_SCAN,
                  NULL));
  if (!file.IsValid()) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to create '" << filename.value() << "'";
    return HRESULT_FROM_WIN32(error);
  }
  DWORD written;
  if (!WriteFile(file, content, static_cast<DWORD>(length), &written, NULL)) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to write to '" << filename.value() << "'";
    return HRESULT_FROM_WIN32(error);
  }
  return S_OK;
}
HRESULT MoveConfigFileFromTemp(const base::FilePath& filename) {
  
  
  base::FilePath tempname = GetTempLocationFor(filename);
  if (!MoveFileExW(tempname.value().c_str(),
                   filename.value().c_str(),
                   MOVEFILE_REPLACE_EXISTING)) {
      DWORD error = GetLastError();
      LOG_GETLASTERROR(ERROR)
          << "Failed to rename '" << tempname.value() << "' to '"
          << filename.value() << "'";
      return HRESULT_FROM_WIN32(error);
  }
  return S_OK;
}
HRESULT WriteConfig(const char* content, size_t length, HWND owner_window) {
  if (length > kMaxConfigFileSize) {
      return E_FAIL;
  }
  
  scoped_ptr<base::Value> config_value(base::JSONReader::Read(content));
  if (!config_value.get()) {
    return E_FAIL;
  }
  base::DictionaryValue* config_dict = NULL;
  if (!config_value->GetAsDictionary(&config_dict)) {
    return E_FAIL;
  }
  std::string email;
  if (!config_dict->GetString(kHostOwner, &email)) {
    if (!config_dict->GetString(kXmppLogin, &email)) {
      return E_FAIL;
    }
  }
  std::string host_id, host_secret_hash;
  if (!config_dict->GetString(kHostId, &host_id) ||
      !config_dict->GetString(kHostSecretHash, &host_secret_hash)) {
    return E_FAIL;
  }
  
  
  if (!IsClientAdmin()) {
    remoting::VerifyConfigWindowWin verify_win(email, host_id,
                                               host_secret_hash);
    DWORD error = verify_win.DoModal(owner_window);
    if (error != ERROR_SUCCESS) {
      return HRESULT_FROM_WIN32(error);
    }
  }
  
  base::DictionaryValue unprivileged_config_dict;
  for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) {
    const char* key = kUnprivilegedConfigKeys[i];
    base::string16 value;
    if (config_dict->GetString(key, &value)) {
      unprivileged_config_dict.SetString(key, value);
    }
  }
  std::string unprivileged_config_str;
  base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str);
  
  base::FilePath full_config_file_path =
      remoting::GetConfigDir().Append(kConfigFileName);
  HRESULT hr = WriteConfigFileToTemp(full_config_file_path,
                                     kConfigFileSecurityDescriptor,
                                     content,
                                     length);
  if (FAILED(hr)) {
    return hr;
  }
  
  base::FilePath unprivileged_config_file_path =
      remoting::GetConfigDir().Append(kUnprivilegedConfigFileName);
  hr = WriteConfigFileToTemp(unprivileged_config_file_path,
                             kUnprivilegedConfigFileSecurityDescriptor,
                             unprivileged_config_str.data(),
                             unprivileged_config_str.size());
  if (FAILED(hr)) {
    return hr;
  }
  
  hr = MoveConfigFileFromTemp(full_config_file_path);
  if (FAILED(hr)) {
    return hr;
  }
  
  hr = MoveConfigFileFromTemp(unprivileged_config_file_path);
  if (FAILED(hr)) {
    return hr;
  }
  return S_OK;
}
} 
ElevatedController::ElevatedController() : owner_window_(NULL) {
}
HRESULT ElevatedController::FinalConstruct() {
  return S_OK;
}
void ElevatedController::FinalRelease() {
}
STDMETHODIMP ElevatedController::GetConfig(BSTR* config_out) {
  base::FilePath config_dir = remoting::GetConfigDir();
  
  scoped_ptr<base::DictionaryValue> config;
  HRESULT hr = ReadConfig(config_dir.Append(kUnprivilegedConfigFileName),
                          &config);
  if (FAILED(hr)) {
    return hr;
  }
  
  std::string file_content;
  base::JSONWriter::Write(config.get(), &file_content);
  *config_out = ::SysAllocString(base::UTF8ToUTF16(file_content).c_str());
  if (config_out == NULL) {
    return E_OUTOFMEMORY;
  }
  return S_OK;
}
STDMETHODIMP ElevatedController::GetVersion(BSTR* version_out) {
  
  
  HMODULE binary = base::GetModuleFromAddress(
      reinterpret_cast<void*>(&ReadConfig));
  scoped_ptr<FileVersionInfo> version_info(
      FileVersionInfo::CreateFileVersionInfoForModule(binary));
  base::string16 version;
  if (version_info.get()) {
    version = version_info->product_version();
  }
  *version_out = ::SysAllocString(version.c_str());
  if (version_out == NULL) {
    return E_OUTOFMEMORY;
  }
  return S_OK;
}
STDMETHODIMP ElevatedController::SetConfig(BSTR config) {
  
  base::FilePath config_dir = remoting::GetConfigDir();
  if (!base::CreateDirectory(config_dir)) {
    return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
  }
  std::string file_content = base::UTF16ToUTF8(
    base::string16(static_cast<base::char16*>(config), ::SysStringLen(config)));
  return WriteConfig(file_content.c_str(), file_content.size(), owner_window_);
}
STDMETHODIMP ElevatedController::SetOwnerWindow(LONG_PTR window_handle) {
  owner_window_ = reinterpret_cast<HWND>(window_handle);
  return S_OK;
}
STDMETHODIMP ElevatedController::StartDaemon() {
  ScopedScHandle service;
  HRESULT hr = OpenService(&service);
  if (FAILED(hr)) {
    return hr;
  }
  
  if (!::ChangeServiceConfigW(service,
                              SERVICE_NO_CHANGE,
                              SERVICE_AUTO_START,
                              SERVICE_NO_CHANGE,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL)) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to change the '" << kWindowsServiceName
        << "'service start type to 'auto'";
    return HRESULT_FROM_WIN32(error);
  }
  
  if (!StartService(service, 0, NULL)) {
    DWORD error = GetLastError();
    if (error != ERROR_SERVICE_ALREADY_RUNNING) {
      LOG_GETLASTERROR(ERROR)
          << "Failed to start the '" << kWindowsServiceName << "'service";
      return HRESULT_FROM_WIN32(error);
    }
  }
  return S_OK;
}
STDMETHODIMP ElevatedController::StopDaemon() {
  ScopedScHandle service;
  HRESULT hr = OpenService(&service);
  if (FAILED(hr)) {
    return hr;
  }
  
  if (!::ChangeServiceConfigW(service,
                              SERVICE_NO_CHANGE,
                              SERVICE_DEMAND_START,
                              SERVICE_NO_CHANGE,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL,
                              NULL)) {
    DWORD error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to change the '" << kWindowsServiceName
        << "'service start type to 'manual'";
    return HRESULT_FROM_WIN32(error);
  }
  
  SERVICE_STATUS status;
  if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
    DWORD error = GetLastError();
    if (error != ERROR_SERVICE_NOT_ACTIVE) {
      LOG_GETLASTERROR(ERROR)
          << "Failed to stop the '" << kWindowsServiceName << "'service";
      return HRESULT_FROM_WIN32(error);
    }
  }
  return S_OK;
}
STDMETHODIMP ElevatedController::UpdateConfig(BSTR config) {
  
  std::string config_str = base::UTF16ToUTF8(
    base::string16(static_cast<base::char16*>(config), ::SysStringLen(config)));
  scoped_ptr<base::Value> config_value(base::JSONReader::Read(config_str));
  if (!config_value.get()) {
    return E_FAIL;
  }
  base::DictionaryValue* config_dict = NULL;
  if (!config_value->GetAsDictionary(&config_dict)) {
    return E_FAIL;
  }
  
  for (int i = 0; i < arraysize(kReadonlyKeys); ++i) {
    if (config_dict->HasKey(kReadonlyKeys[i])) {
      return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
    }
  }
  
  base::FilePath config_dir = remoting::GetConfigDir();
  scoped_ptr<base::DictionaryValue> config_old;
  HRESULT hr = ReadConfig(config_dir.Append(kConfigFileName), &config_old);
  if (FAILED(hr)) {
    return hr;
  }
  
  config_old->MergeDictionary(config_dict);
  
  std::string config_updated_str;
  base::JSONWriter::Write(config_old.get(), &config_updated_str);
  return WriteConfig(config_updated_str.c_str(), config_updated_str.size(),
                     owner_window_);
}
STDMETHODIMP ElevatedController::GetUsageStatsConsent(BOOL* allowed,
                                                         BOOL* set_by_policy) {
  bool local_allowed;
  bool local_set_by_policy;
  if (remoting::GetUsageStatsConsent(&local_allowed, &local_set_by_policy)) {
    *allowed = local_allowed;
    *set_by_policy = local_set_by_policy;
    return S_OK;
  } else {
    return E_FAIL;
  }
}
STDMETHODIMP ElevatedController::SetUsageStatsConsent(BOOL allowed) {
  if (remoting::SetUsageStatsConsent(!!allowed)) {
    return S_OK;
  } else {
    return E_FAIL;
  }
}
HRESULT ElevatedController::OpenService(ScopedScHandle* service_out) {
  DWORD error;
  ScopedScHandle scmanager(
      ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
                       SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
  if (!scmanager.IsValid()) {
    error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to connect to the service control manager";
    return HRESULT_FROM_WIN32(error);
  }
  DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
                         SERVICE_START | SERVICE_STOP;
  ScopedScHandle service(
      ::OpenServiceW(scmanager, kWindowsServiceName, desired_access));
  if (!service.IsValid()) {
    error = GetLastError();
    LOG_GETLASTERROR(ERROR)
        << "Failed to open to the '" << kWindowsServiceName << "' service";
    return HRESULT_FROM_WIN32(error);
  }
  service_out->Set(service.Take());
  return S_OK;
}
}