// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
#include <string>
#include <vector>
#include "base/base_paths_win.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/win_constants.h"
namespace device_management_storage {
namespace {
// Registry for device ID.
constexpr wchar_t kRegKeyCryptographyKey[] =
L"SOFTWARE\\Microsoft\\Cryptography\\";
constexpr wchar_t kRegValueMachineGuid[] = L"MachineGuid";
bool ReadRegistryString(const std::wstring& key_path,
const std::wstring& name,
REGSAM reg_view,
std::string& value) {
std::wstring data;
const LONG result = base::win::RegKey(HKEY_LOCAL_MACHINE, key_path.c_str(),
reg_view | KEY_READ)
.ReadValue(name.c_str(), &data);
if (result != ERROR_SUCCESS) {
VLOG(1) << __func__ << ": failed to read registry: " << key_path << "@"
<< name;
return false;
}
value = base::SysWideToUTF8(data);
return true;
}
bool DeleteRegistryValue(const std::wstring& key_path,
const std::wstring& name,
REGSAM reg_view) {
base::win::RegKey key;
if (const LONG result = key.Open(HKEY_LOCAL_MACHINE, key_path.c_str(),
reg_view | KEY_SET_VALUE);
result != ERROR_SUCCESS) {
return result == ERROR_FILE_NOT_FOUND;
}
if (const LONG result = key.DeleteValue(name.c_str());
result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
VLOG(1) << "Failed to delete registry: " << key_path << "@" << name;
return false;
}
return true;
}
// Reads a token from the registry value of string type .
bool ReadTokenString(const std::wstring& key_path,
const std::wstring& name,
REGSAM reg_view,
std::string& token) {
if (!ReadRegistryString(key_path, name, reg_view, token)) {
return false;
}
if (token.size() > DMStorage::kMaxDmTokenLength) {
token.clear();
VLOG(1) << __func__ << ": token [" << token << "] is too long.";
return false;
}
return true;
}
// Reads a token from the registry value of type REG_BINARY.
bool ReadTokenBinary(const std::wstring& key_path,
const std::wstring& name,
REGSAM reg_view,
std::string& token) {
base::win::RegKey key;
if (key.Open(HKEY_LOCAL_MACHINE, key_path.c_str(), reg_view | KEY_READ) !=
ERROR_SUCCESS) {
return false;
}
DWORD size = 0;
DWORD type = 0;
LONG error = key.ReadValue(name.c_str(), nullptr, &size, &type);
if (error != ERROR_SUCCESS) {
VLOG(2) << "ReadValue failed: " << error;
return false;
}
if (size == 0) {
VLOG(2) << "The token is empty.";
return false;
}
if (size > DMStorage::kMaxDmTokenLength) {
VLOG(2) << "Value is too large: " << size;
return false;
}
if (type != REG_BINARY) {
VLOG(2) << "Ignored token value with incompatible type.";
return false;
}
std::vector<char> value(size);
error = key.ReadValue(name.c_str(), &value.front(), &size, &type);
if (error != ERROR_SUCCESS) {
VLOG(2) << "ReadValue failed: " << error;
return false;
}
token.assign(value.begin(), value.end());
return true;
}
// Writes a token as a binary value to the registry.
bool WriteTokenBinary(const std::wstring& key_path,
const std::wstring& name,
REGSAM reg_view,
const std::string& token) {
if (token.size() > DMStorage::kMaxDmTokenLength) {
VLOG(2) << "Value is too large: " << token.size();
return false;
}
base::win::RegKey key;
LONG error =
key.Create(HKEY_LOCAL_MACHINE, key_path.c_str(), reg_view | KEY_WRITE);
if (error != ERROR_SUCCESS) {
VLOG(1) << "Failed to open " << key_path << ": " << error;
return false;
}
error = key.WriteValue(name.c_str(), token.data(), token.size(), REG_BINARY);
if (error != ERROR_SUCCESS) {
VLOG(1) << "Failed to write " << key_path << " @ " << name
<< " (binary): " << error;
}
return error == ERROR_SUCCESS;
}
class TokenService : public TokenServiceInterface {
public:
TokenService() = default;
~TokenService() override = default;
// Overrides for TokenServiceInterface.
std::string GetDeviceID() const override;
bool IsEnrollmentMandatory() const override;
bool StoreEnrollmentToken(const std::string& enrollment_token) override;
bool DeleteEnrollmentToken() override;
std::string GetEnrollmentToken() const override;
bool StoreDmToken(const std::string& dm_token) override;
bool DeleteDmToken() override;
std::string GetDmToken() const override;
};
std::string TokenService::GetDeviceID() const {
std::string device_id;
if (!ReadRegistryString(kRegKeyCryptographyKey, kRegValueMachineGuid,
KEY_READ | KEY_WOW64_64KEY, device_id)) {
return {};
}
return device_id;
}
bool TokenService::IsEnrollmentMandatory() const {
DWORD is_mandatory = 0;
base::win::RegKey key;
return (key.Open(HKEY_LOCAL_MACHINE, updater::kRegKeyCompanyCloudManagement,
updater::Wow6432(KEY_READ)) == ERROR_SUCCESS &&
key.ReadValueDW(updater::kRegValueEnrollmentMandatory,
&is_mandatory) == ERROR_SUCCESS)
? is_mandatory
: false;
}
bool TokenService::StoreEnrollmentToken(const std::string& token) {
const bool result = updater::SetRegistryKey(
HKEY_LOCAL_MACHINE, updater::kRegKeyCompanyCloudManagement,
updater::kRegValueEnrollmentToken, base::SysUTF8ToWide(token));
VLOG(1) << "Update enrollment token to: [" << token
<< "], bool result=" << result;
return result;
}
bool TokenService::DeleteEnrollmentToken() {
VLOG(1) << __func__;
return DeleteRegistryValue(updater::kRegKeyCompanyCloudManagement,
updater::kRegValueEnrollmentToken,
KEY_WOW64_32KEY) &&
DeleteRegistryValue(updater::kRegKeyCompanyLegacyCloudManagement,
updater::kRegValueCloudManagementEnrollmentToken,
KEY_WOW64_32KEY);
}
std::string TokenService::GetEnrollmentToken() const {
std::string token;
if (ReadTokenString(updater::kRegKeyCompanyCloudManagement,
updater::kRegValueEnrollmentToken, KEY_WOW64_32KEY,
token)) {
return token;
}
for (const std::wstring& key_path :
{std::wstring(updater::kRegKeyCompanyLegacyCloudManagement),
updater::GetAppClientsKey(UPDATER_APPID),
updater::GetAppClientsKey(updater::kLegacyGoogleUpdateAppID)}) {
if (ReadTokenString(key_path,
updater::kRegValueCloudManagementEnrollmentToken,
KEY_WOW64_32KEY, token)) {
return token;
}
}
return {};
}
bool TokenService::StoreDmToken(const std::string& token) {
VLOG(1) << __func__ << ": [" << token << "]";
return WriteTokenBinary(updater::kRegKeyCompanyEnrollment,
updater::kRegValueDmToken, KEY_WOW64_32KEY, token) &&
WriteTokenBinary(updater::kRegKeyCompanyLegacyEnrollment,
updater::kRegValueDmToken, KEY_WOW64_64KEY, token);
}
bool TokenService::DeleteDmToken() {
VLOG(1) << __func__;
return DeleteRegistryValue(updater::kRegKeyCompanyEnrollment,
updater::kRegValueDmToken, KEY_WOW64_32KEY) &&
DeleteRegistryValue(updater::kRegKeyCompanyLegacyEnrollment,
updater::kRegValueDmToken, KEY_WOW64_64KEY);
}
std::string TokenService::GetDmToken() const {
std::string token;
if (ReadTokenBinary(updater::kRegKeyCompanyEnrollment,
updater::kRegValueDmToken, KEY_WOW64_32KEY, token) ||
ReadTokenBinary(updater::kRegKeyCompanyLegacyEnrollment,
updater::kRegValueDmToken, KEY_WOW64_64KEY, token)) {
return token;
}
VLOG(1) << __func__ << ": token not found.";
return {};
}
} // namespace
scoped_refptr<DMStorage> CreateDMStorage(
const base::FilePath& policy_cache_root) {
return CreateDMStorage(policy_cache_root, std::make_unique<TokenService>());
}
scoped_refptr<DMStorage> GetDefaultDMStorage() {
base::FilePath program_filesx86_dir;
if (!base::PathService::Get(base::DIR_PROGRAM_FILESX86,
&program_filesx86_dir)) {
return nullptr;
}
return CreateDMStorage(
program_filesx86_dir.AppendASCII(COMPANY_SHORTNAME_STRING)
.AppendASCII("Policies"));
}
} // namespace device_management_storage