Index: remoting/host/win/elevated_controller.cc |
diff --git a/remoting/host/win/elevated_controller.cc b/remoting/host/win/elevated_controller.cc |
deleted file mode 100644 |
index 8cbfcf8273f7908ba730cc2cee1d0790155d3e61..0000000000000000000000000000000000000000 |
--- a/remoting/host/win/elevated_controller.cc |
+++ /dev/null |
@@ -1,531 +0,0 @@ |
-// Copyright (c) 2012 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/elevated_controller.h" |
- |
-#include "base/file_version_info.h" |
-#include "base/files/file_util.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/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/host_config.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 { |
- |
-// The maximum size of the configuration file. "1MB ought to be enough" for any |
-// reasonable configuration we will ever need. 1MB is low enough to make |
-// the probability of out of memory situation fairly low. OOM is still possible |
-// and we will crash if it occurs. |
-const size_t kMaxConfigFileSize = 1024 * 1024; |
- |
-// The host configuration file name. |
-const base::FilePath::CharType kConfigFileName[] = FILE_PATH_LITERAL("host.json"); |
- |
-// The unprivileged configuration file name. |
-const base::FilePath::CharType kUnprivilegedConfigFileName[] = |
- FILE_PATH_LITERAL("host_unprivileged.json"); |
- |
-// The extension for the temporary file. |
-const base::FilePath::CharType kTempFileExtension[] = FILE_PATH_LITERAL("json~"); |
- |
-// The host configuration file security descriptor that enables full access to |
-// Local System and built-in administrators only. |
-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)"; |
- |
-// Configuration keys. |
- |
-// The configuration keys that cannot be specified in UpdateConfig(). |
-const char* const kReadonlyKeys[] = { |
- kHostIdConfigPath, kHostOwnerConfigPath, kHostOwnerEmailConfigPath, |
- kXmppLoginConfigPath }; |
- |
-// The configuration keys whose values may be read by GetConfig(). |
-const char* const kUnprivilegedConfigKeys[] = { |
- kHostIdConfigPath, kXmppLoginConfigPath }; |
- |
-// Determines if the client runs in the security context that allows performing |
-// administrative tasks (i.e. the user belongs to the adminstrators group and |
-// the client runs elevated). |
-bool IsClientAdmin() { |
- HRESULT hr = CoImpersonateClient(); |
- if (FAILED(hr)) { |
- return false; |
- } |
- |
- SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; |
- PSID administrators_group = nullptr; |
- 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(nullptr, administrators_group, &result)) { |
- result = false; |
- } |
- FreeSid(administrators_group); |
- } |
- |
- hr = CoRevertToSelf(); |
- CHECK(SUCCEEDED(hr)); |
- |
- return !!result; |
-} |
- |
-// Reads and parses the configuration file up to |kMaxConfigFileSize| in |
-// size. |
-HRESULT ReadConfig(const base::FilePath& filename, |
- scoped_ptr<base::DictionaryValue>* config_out) { |
- |
- // Read raw data from the configuration file. |
- base::win::ScopedHandle file( |
- CreateFileW(filename.value().c_str(), |
- GENERIC_READ, |
- FILE_SHARE_READ | FILE_SHARE_WRITE, |
- nullptr, |
- OPEN_EXISTING, |
- FILE_FLAG_SEQUENTIAL_SCAN, |
- nullptr)); |
- |
- if (!file.IsValid()) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to open '" << filename.value() << "'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- scoped_ptr<char[]> buffer(new char[kMaxConfigFileSize]); |
- DWORD size = kMaxConfigFileSize; |
- if (!::ReadFile(file.Get(), &buffer[0], size, &size, nullptr)) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to read '" << filename.value() << "'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- // Parse the JSON configuration, expecting it to contain a dictionary. |
- 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() == nullptr || !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); |
-} |
- |
-// Writes a config file to a temporary location. |
-HRESULT WriteConfigFileToTemp(const base::FilePath& filename, |
- const char* security_descriptor, |
- const char* content, |
- size_t length) { |
- // Create the security descriptor for the configuration file. |
- ScopedSd sd = ConvertSddlToSd(security_descriptor); |
- if (!sd) { |
- DWORD error = GetLastError(); |
- PLOG(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; |
- |
- // Create a temporary file and write configuration to it. |
- 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, |
- nullptr)); |
- |
- if (!file.IsValid()) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to create '" << filename.value() << "'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- DWORD written; |
- if (!WriteFile(file.Get(), content, static_cast<DWORD>(length), &written, |
- nullptr)) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to write to '" << filename.value() << "'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- return S_OK; |
-} |
- |
-// Moves a config file from its temporary location to its permanent location. |
-HRESULT MoveConfigFileFromTemp(const base::FilePath& filename) { |
- // Now that the configuration is stored successfully replace the actual |
- // configuration file. |
- base::FilePath tempname = GetTempLocationFor(filename); |
- if (!MoveFileExW(tempname.value().c_str(), |
- filename.value().c_str(), |
- MOVEFILE_REPLACE_EXISTING)) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to rename '" << tempname.value() << "' to '" |
- << filename.value() << "'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- return S_OK; |
-} |
- |
-// Writes the configuration file up to |kMaxConfigFileSize| in size. |
-HRESULT WriteConfig(const char* content, size_t length, HWND owner_window) { |
- if (length > kMaxConfigFileSize) { |
- return E_FAIL; |
- } |
- |
- // Extract the configuration data that the user will verify. |
- scoped_ptr<base::Value> config_value(base::JSONReader::Read(content)); |
- if (!config_value.get()) { |
- return E_FAIL; |
- } |
- base::DictionaryValue* config_dict = nullptr; |
- if (!config_value->GetAsDictionary(&config_dict)) { |
- return E_FAIL; |
- } |
- std::string email; |
- if (!config_dict->GetString(kHostOwnerEmailConfigPath, &email)) { |
- if (!config_dict->GetString(kHostOwnerConfigPath, &email)) { |
- if (!config_dict->GetString(kXmppLoginConfigPath, &email)) { |
- return E_FAIL; |
- } |
- } |
- } |
- std::string host_id, host_secret_hash; |
- if (!config_dict->GetString(kHostIdConfigPath, &host_id) || |
- !config_dict->GetString(kHostSecretHashConfigPath, &host_secret_hash)) { |
- return E_FAIL; |
- } |
- |
- // Ask the user to verify the configuration (unless the client is admin |
- // already). |
- 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); |
- } |
- } |
- |
- // Extract the unprivileged fields from the configuration. |
- 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); |
- |
- // Write the full configuration file to a temporary location. |
- 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; |
- } |
- |
- // Write the unprivileged configuration file to a temporary location. |
- 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; |
- } |
- |
- // Move the full configuration file to its permanent location. |
- hr = MoveConfigFileFromTemp(full_config_file_path); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- // Move the unprivileged configuration file to its permanent location. |
- hr = MoveConfigFileFromTemp(unprivileged_config_file_path); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-} // namespace |
- |
-ElevatedController::ElevatedController() : owner_window_(nullptr) { |
-} |
- |
-HRESULT ElevatedController::FinalConstruct() { |
- return S_OK; |
-} |
- |
-void ElevatedController::FinalRelease() { |
-} |
- |
-STDMETHODIMP ElevatedController::GetConfig(BSTR* config_out) { |
- base::FilePath config_dir = remoting::GetConfigDir(); |
- |
- // Read the unprivileged part of host configuration. |
- scoped_ptr<base::DictionaryValue> config; |
- HRESULT hr = ReadConfig(config_dir.Append(kUnprivilegedConfigFileName), |
- &config); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- // Convert the config back to a string and return it to the caller. |
- std::string file_content; |
- base::JSONWriter::Write(config.get(), &file_content); |
- |
- *config_out = ::SysAllocString(base::UTF8ToUTF16(file_content).c_str()); |
- if (config_out == nullptr) { |
- return E_OUTOFMEMORY; |
- } |
- |
- return S_OK; |
-} |
- |
-STDMETHODIMP ElevatedController::GetVersion(BSTR* version_out) { |
- // Report the product version number of the daemon controller binary as |
- // the host version. |
- 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 == nullptr) { |
- return E_OUTOFMEMORY; |
- } |
- |
- return S_OK; |
-} |
- |
-STDMETHODIMP ElevatedController::SetConfig(BSTR config) { |
- // Determine the config directory path and create it if necessary. |
- 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; |
- } |
- |
- // Change the service start type to 'auto'. |
- if (!::ChangeServiceConfigW(service.Get(), |
- SERVICE_NO_CHANGE, |
- SERVICE_AUTO_START, |
- SERVICE_NO_CHANGE, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr)) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName |
- << "'service start type to 'auto'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- // Start the service. |
- if (!StartService(service.Get(), 0, nullptr)) { |
- DWORD error = GetLastError(); |
- if (error != ERROR_SERVICE_ALREADY_RUNNING) { |
- PLOG(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; |
- } |
- |
- // Change the service start type to 'manual'. |
- if (!::ChangeServiceConfigW(service.Get(), |
- SERVICE_NO_CHANGE, |
- SERVICE_DEMAND_START, |
- SERVICE_NO_CHANGE, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr, |
- nullptr)) { |
- DWORD error = GetLastError(); |
- PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName |
- << "'service start type to 'manual'"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- // Stop the service. |
- SERVICE_STATUS status; |
- if (!ControlService(service.Get(), SERVICE_CONTROL_STOP, &status)) { |
- DWORD error = GetLastError(); |
- if (error != ERROR_SERVICE_NOT_ACTIVE) { |
- PLOG(ERROR) << "Failed to stop the '" << kWindowsServiceName |
- << "'service"; |
- return HRESULT_FROM_WIN32(error); |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-STDMETHODIMP ElevatedController::UpdateConfig(BSTR config) { |
- // Parse the 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 = nullptr; |
- if (!config_value->GetAsDictionary(&config_dict)) { |
- return E_FAIL; |
- } |
- // Check for bad keys. |
- for (int i = 0; i < arraysize(kReadonlyKeys); ++i) { |
- if (config_dict->HasKey(kReadonlyKeys[i])) { |
- return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
- } |
- } |
- // Get the old config. |
- 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; |
- } |
- // Merge items from the given config into the old config. |
- config_old->MergeDictionary(config_dict); |
- // Write the updated config. |
- 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(nullptr, SERVICES_ACTIVE_DATABASE, |
- SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); |
- if (!scmanager.IsValid()) { |
- error = GetLastError(); |
- PLOG(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.Get(), kWindowsServiceName, desired_access)); |
- if (!service.IsValid()) { |
- error = GetLastError(); |
- PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName |
- << "' service"; |
- |
- return HRESULT_FROM_WIN32(error); |
- } |
- |
- service_out->Set(service.Take()); |
- return S_OK; |
-} |
- |
-} // namespace remoting |