#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "components/policy/core/common/registry_dict.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/numerics/byte_conversions.h"
#include "base/strings/cstring_view.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/common/schema.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/registry.h"
#include "base/win/win_util.h"
using base::win::RegistryKeyIterator;
using base::win::RegistryValueIterator;
#endif
namespace policy {
namespace {
bool IsKeyNumerical(const std::string& key) { … }
}
std::optional<base::Value> ConvertRegistryValue(const base::Value& value,
const Schema& schema) { … }
bool CaseInsensitiveStringCompare::operator()(const std::string& a,
const std::string& b) const { … }
RegistryDict::RegistryDict() = default;
RegistryDict::~RegistryDict() { … }
RegistryDict* RegistryDict::GetKey(const std::string& name) { … }
const RegistryDict* RegistryDict::GetKey(const std::string& name) const { … }
void RegistryDict::SetKey(const std::string& name,
std::unique_ptr<RegistryDict> dict) { … }
std::unique_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) { … }
void RegistryDict::ClearKeys() { … }
base::Value* RegistryDict::GetValue(const std::string& name) { … }
const base::Value* RegistryDict::GetValue(const std::string& name) const { … }
void RegistryDict::SetValue(const std::string& name, base::Value&& dict) { … }
std::optional<base::Value> RegistryDict::RemoveValue(const std::string& name) { … }
void RegistryDict::ClearValues() { … }
void RegistryDict::Merge(const RegistryDict& other) { … }
void RegistryDict::Swap(RegistryDict* other) { … }
#if BUILDFLAG(IS_WIN)
void RegistryDict::ReadRegistry(HKEY hive, const std::wstring& root) {
ClearKeys();
ClearValues();
for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
const std::string name = base::WideToUTF8(it.Name());
switch (it.Type()) {
case REG_EXPAND_SZ:
if (auto expanded_path = base::win::ExpandEnvironmentVariables(
base::wcstring_view{it.Value(), wcslen(it.Value())})) {
SetValue(name, base::Value(base::WideToUTF8(*expanded_path)));
continue;
}
[[fallthrough]];
case REG_SZ:
SetValue(name, base::Value(base::WideToUTF8(it.Value())));
continue;
case REG_DWORD_LITTLE_ENDIAN:
case REG_DWORD_BIG_ENDIAN:
if (it.ValueSize() == sizeof(DWORD)) {
auto value =
UNSAFE_TODO(
base::span(reinterpret_cast<const uint8_t*>(it.Value()),
it.ValueSize()))
.first<sizeof(DWORD)>();
DWORD dword_value = it.Type() == REG_DWORD_BIG_ENDIAN
? base::U32FromBigEndian(value)
: base::U32FromLittleEndian(value);
SetValue(name, base::Value(static_cast<int>(dword_value)));
continue;
}
[[fallthrough]];
case REG_NONE:
case REG_LINK:
case REG_MULTI_SZ:
case REG_RESOURCE_LIST:
case REG_FULL_RESOURCE_DESCRIPTOR:
case REG_RESOURCE_REQUIREMENTS_LIST:
case REG_QWORD_LITTLE_ENDIAN:
break;
}
LOG(WARNING) << "Failed to read hive " << hive << " at " << root << "\\"
<< name << " type " << it.Type();
}
for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
std::string name(base::WideToUTF8(it.Name()));
std::unique_ptr<RegistryDict> subdict(new RegistryDict());
subdict->ReadRegistry(hive, root + L"\\" + it.Name());
SetKey(name, std::move(subdict));
}
}
std::optional<base::Value> RegistryDict::ConvertToJSON(
const Schema& schema) const {
base::Value::Type type =
schema.valid() ? schema.type() : base::Value::Type::DICT;
switch (type) {
case base::Value::Type::DICT: {
base::Value::Dict result;
for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
entry != values_.end(); ++entry) {
SchemaList matching_schemas =
schema.valid() ? schema.GetMatchingProperties(entry->first)
: SchemaList();
if (matching_schemas.empty())
matching_schemas.push_back(Schema());
for (const Schema& subschema : matching_schemas) {
std::optional<base::Value> converted =
ConvertRegistryValue(entry->second, subschema);
if (converted.has_value()) {
result.Set(entry->first, std::move(converted.value()));
break;
}
}
}
for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
entry != keys_.end(); ++entry) {
SchemaList matching_schemas =
schema.valid() ? schema.GetMatchingProperties(entry->first)
: SchemaList();
if (matching_schemas.empty())
matching_schemas.push_back(Schema());
for (const Schema& subschema : matching_schemas) {
std::optional<base::Value> converted =
entry->second->ConvertToJSON(subschema);
if (converted) {
result.Set(entry->first, std::move(*converted));
break;
}
}
}
return base::Value(std::move(result));
}
case base::Value::Type::LIST: {
base::Value::List result;
Schema item_schema = schema.valid() ? schema.GetItems() : Schema();
for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
entry != keys_.end(); ++entry) {
if (!IsKeyNumerical(entry->first))
continue;
std::optional<base::Value> converted =
entry->second->ConvertToJSON(item_schema);
if (converted)
result.Append(std::move(*converted));
}
for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
entry != values_.end(); ++entry) {
if (!IsKeyNumerical(entry->first))
continue;
std::optional<base::Value> converted =
ConvertRegistryValue(entry->second, item_schema);
if (converted.has_value())
result.Append(std::move(*converted));
}
return base::Value(std::move(result));
}
default:
LOG(WARNING) << "Can't convert registry key to schema type " << type;
}
return std::nullopt;
}
#endif
}