blob: 925a826fc6cbfe54fceea670e1c3e64b79a8c9e8 [file] [log] [blame]
// Copyright (c) 2014 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 "components/policy/core/common/policy_loader_win.h"
#include <windows.h>
#include <lm.h> // For limits.
#include <ntdsapi.h> // For Ds[Un]Bind
#include <rpc.h> // For struct GUID
#include <shlwapi.h> // For PathIsUNC()
#include <stddef.h>
#include <userenv.h> // For GPO functions
#include <memory>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/scoped_native_library.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "components/json_schema/json_schema_constants.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_load_status.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/core/common/preg_parser_win.h"
#include "components/policy/core/common/registry_dict_win.h"
#include "components/policy/core/common/schema.h"
#include "policy/policy_constants.h"
namespace schema = json_schema_constants;
namespace policy {
namespace {
const char kKeyMandatory[] = "policy";
const char kKeyRecommended[] = "recommended";
const char kKeySchema[] = "schema";
const char kKeyThirdParty[] = "3rdparty";
// The Legacy Browser Support was the first user of the policy-for-extensions
// API, and relied on behavior that will be phased out. If this extension is
// present then its policies will be loaded in a special way.
// TODO(joaodasilva): remove this for M35. http://crbug.com/325349
const char kLegacyBrowserSupportExtensionId[] =
"heildphpnddilhkemkielfhnkaagiabh";
// The web store url that is the only trusted source for extensions.
const char kExpectedWebStoreUrl[] =
";https://clients2.google.com/service/update2/crx";
// String to be prepended to each blocked entry.
const char kBlockedExtensionPrefix[] = "[BLOCKED]";
// List of policies that are considered only if the user is part of a AD domain.
// Please document any new additions in policy_templates.json!
const char* kInsecurePolicies[] = {
key::kMetricsReportingEnabled,
key::kDefaultSearchProviderEnabled,
key::kHomepageIsNewTabPage,
key::kHomepageLocation,
key::kRestoreOnStartup,
key::kRestoreOnStartupURLs
};
#pragma warning(push)
#pragma warning(disable: 4068) // unknown pragmas
// TODO(dcheng): Remove pragma once http://llvm.org/PR24007 is fixed.
#pragma clang diagnostic ignored "-Wmissing-braces"
// The GUID of the registry settings group policy extension.
GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID;
#pragma warning(pop)
// The list of possible errors that can occur while collecting information about
// the current enterprise environment.
// This enum is used to define the buckets for an enumerated UMA histogram.
// Hence,
// (a) existing enumerated constants should never be deleted or reordered, and
// (b) new constants should only be appended at the end of the enumeration.
enum DomainCheckErrors {
DOMAIN_CHECK_ERROR_GET_JOIN_INFO = 0,
DOMAIN_CHECK_ERROR_DS_BIND = 1,
DOMAIN_CHECK_ERROR_SIZE, // Not a DomainCheckError. Must be last.
};
// If the LBS extension is found and contains a schema in the registry then this
// function is used to patch it, and make it compliant. The fix is to
// add an "items" attribute to lists that don't declare it.
std::string PatchSchema(const std::string& schema) {
base::JSONParserOptions options = base::JSON_PARSE_RFC;
std::unique_ptr<base::Value> json = base::JSONReader::Read(schema, options);
base::DictionaryValue* dict = NULL;
base::DictionaryValue* properties = NULL;
if (!json ||
!json->GetAsDictionary(&dict) ||
!dict->GetDictionary(schema::kProperties, &properties)) {
return schema;
}
for (base::DictionaryValue::Iterator it(*properties);
!it.IsAtEnd(); it.Advance()) {
base::DictionaryValue* policy_schema = NULL;
std::string type;
if (properties->GetDictionary(it.key(), &policy_schema) &&
policy_schema->GetString(schema::kType, &type) &&
type == schema::kArray &&
!policy_schema->HasKey(schema::kItems)) {
std::unique_ptr<base::DictionaryValue> items(new base::DictionaryValue());
items->SetString(schema::kType, schema::kString);
policy_schema->Set(schema::kItems, items.release());
}
}
std::string serialized;
base::JSONWriter::Write(*json, &serialized);
return serialized;
}
// Verifies that untrusted policies contain only safe values. Modifies the
// |policy| in place.
void FilterUntrustedPolicy(PolicyMap* policy) {
if (base::win::IsEnrolledToDomain())
return;
int invalid_policies = 0;
const PolicyMap::Entry* map_entry =
policy->Get(key::kExtensionInstallForcelist);
if (map_entry && map_entry->value) {
const base::ListValue* policy_list_value = NULL;
if (!map_entry->value->GetAsList(&policy_list_value))
return;
std::unique_ptr<base::ListValue> filtered_values(new base::ListValue);
for (const auto& list_entry : *policy_list_value) {
std::string entry;
if (!list_entry->GetAsString(&entry))
continue;
size_t pos = entry.find(';');
if (pos == std::string::npos)
continue;
// Only allow custom update urls in enterprise environments.
if (!base::LowerCaseEqualsASCII(entry.substr(pos),
kExpectedWebStoreUrl)) {
entry = kBlockedExtensionPrefix + entry;
invalid_policies++;
}
filtered_values->AppendString(entry);
}
if (invalid_policies) {
PolicyMap::Entry filtered_entry = map_entry->DeepCopy();
filtered_entry.value = std::move(filtered_values);
policy->Set(key::kExtensionInstallForcelist, std::move(filtered_entry));
const PolicyDetails* details = GetChromePolicyDetails(
key::kExtensionInstallForcelist);
UMA_HISTOGRAM_SPARSE_SLOWLY("EnterpriseCheck.InvalidPolicies",
details->id);
}
}
for (size_t i = 0; i < arraysize(kInsecurePolicies); ++i) {
if (policy->Get(kInsecurePolicies[i])) {
// TODO(pastarmovj): Surface this issue in the about:policy page.
policy->Erase(kInsecurePolicies[i]);
invalid_policies++;
const PolicyDetails* details =
GetChromePolicyDetails(kInsecurePolicies[i]);
UMA_HISTOGRAM_SPARSE_SLOWLY("EnterpriseCheck.InvalidPolicies",
details->id);
}
}
UMA_HISTOGRAM_COUNTS("EnterpriseCheck.InvalidPoliciesDetected",
invalid_policies);
}
// A helper class encapsulating run-time-linked function calls to Wow64 APIs.
class Wow64Functions {
public:
Wow64Functions()
: kernel32_lib_(base::FilePath(L"kernel32")),
is_wow_64_process_(NULL),
wow_64_disable_wow_64_fs_redirection_(NULL),
wow_64_revert_wow_64_fs_redirection_(NULL) {
if (kernel32_lib_.is_valid()) {
is_wow_64_process_ = reinterpret_cast<IsWow64Process>(
kernel32_lib_.GetFunctionPointer("IsWow64Process"));
wow_64_disable_wow_64_fs_redirection_ =
reinterpret_cast<Wow64DisableWow64FSRedirection>(
kernel32_lib_.GetFunctionPointer(
"Wow64DisableWow64FsRedirection"));
wow_64_revert_wow_64_fs_redirection_ =
reinterpret_cast<Wow64RevertWow64FSRedirection>(
kernel32_lib_.GetFunctionPointer(
"Wow64RevertWow64FsRedirection"));
}
}
bool is_valid() {
return is_wow_64_process_ &&
wow_64_disable_wow_64_fs_redirection_ &&
wow_64_revert_wow_64_fs_redirection_;
}
bool IsWow64() {
BOOL result = 0;
if (!is_wow_64_process_(GetCurrentProcess(), &result))
PLOG(WARNING) << "IsWow64ProcFailed";
return !!result;
}
bool DisableFsRedirection(PVOID* previous_state) {
return !!wow_64_disable_wow_64_fs_redirection_(previous_state);
}
bool RevertFsRedirection(PVOID previous_state) {
return !!wow_64_revert_wow_64_fs_redirection_(previous_state);
}
private:
typedef BOOL (WINAPI* IsWow64Process)(HANDLE, PBOOL);
typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*);
typedef BOOL (WINAPI* Wow64RevertWow64FSRedirection)(PVOID);
base::ScopedNativeLibrary kernel32_lib_;
IsWow64Process is_wow_64_process_;
Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_;
Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_;
DISALLOW_COPY_AND_ASSIGN(Wow64Functions);
};
// Global Wow64Function instance used by ScopedDisableWow64Redirection below.
static base::LazyInstance<Wow64Functions> g_wow_64_functions =
LAZY_INSTANCE_INITIALIZER;
// Scoper that switches off Wow64 File System Redirection during its lifetime.
class ScopedDisableWow64Redirection {
public:
ScopedDisableWow64Redirection()
: active_(false),
previous_state_(NULL) {
Wow64Functions* wow64 = g_wow_64_functions.Pointer();
if (wow64->is_valid() && wow64->IsWow64()) {
if (wow64->DisableFsRedirection(&previous_state_))
active_ = true;
else
PLOG(WARNING) << "Wow64DisableWow64FSRedirection";
}
}
~ScopedDisableWow64Redirection() {
if (active_)
CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_));
}
bool is_active() { return active_; }
private:
bool active_;
PVOID previous_state_;
DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection);
};
// AppliedGPOListProvider implementation that calls actual Windows APIs.
class WinGPOListProvider : public AppliedGPOListProvider {
public:
~WinGPOListProvider() override {}
// AppliedGPOListProvider:
DWORD GetAppliedGPOList(DWORD flags,
LPCTSTR machine_name,
PSID sid_user,
GUID* extension_guid,
PGROUP_POLICY_OBJECT* gpo_list) override {
return ::GetAppliedGPOList(flags, machine_name, sid_user, extension_guid,
gpo_list);
}
BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) override {
return ::FreeGPOList(gpo_list);
}
};
// The default windows GPO list provider used for PolicyLoaderWin.
static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider =
LAZY_INSTANCE_INITIALIZER;
// Parses |gpo_dict| according to |schema| and writes the resulting policy
// settings to |policy| for the given |scope| and |level|.
void ParsePolicy(const RegistryDict* gpo_dict,
PolicyLevel level,
PolicyScope scope,
const Schema& schema,
PolicyMap* policy) {
if (!gpo_dict)
return;
std::unique_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema));
const base::DictionaryValue* policy_dict = NULL;
if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) {
LOG(WARNING) << "Root policy object is not a dictionary!";
return;
}
policy->LoadFrom(policy_dict, level, scope, POLICY_SOURCE_PLATFORM);
}
// Collects stats about the enterprise environment that can be used to decide
// how to parse the existing policy information.
void CollectEnterpriseUMAs() {
// Collect statistics about the windows suite.
UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.OSType",
base::win::OSInfo::GetInstance()->version_type(),
base::win::SUITE_LAST);
// Get the computer's domain status.
LPWSTR domain;
NETSETUP_JOIN_STATUS join_status;
if (NERR_Success != ::NetGetJoinInformation(NULL, &domain, &join_status)) {
UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed",
DOMAIN_CHECK_ERROR_GET_JOIN_INFO,
DOMAIN_CHECK_ERROR_SIZE);
return;
}
::NetApiBufferFree(domain);
bool in_domain = join_status == NetSetupDomainName;
UMA_HISTOGRAM_BOOLEAN("EnterpriseCheck.InDomain", in_domain);
if (in_domain) {
// This check will tell us how often are domain computers actually
// connected to the enterprise network while Chrome is running.
HANDLE server_bind;
if (ERROR_SUCCESS == ::DsBind(NULL, NULL, &server_bind)) {
UMA_HISTOGRAM_COUNTS("EnterpriseCheck.DomainBindSucceeded", 1);
::DsUnBind(&server_bind);
} else {
UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed",
DOMAIN_CHECK_ERROR_DS_BIND,
DOMAIN_CHECK_ERROR_SIZE);
}
}
}
} // namespace
const base::FilePath::CharType PolicyLoaderWin::kPRegFileName[] =
FILE_PATH_LITERAL("Registry.pol");
PolicyLoaderWin::PolicyLoaderWin(
scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::string16& chrome_policy_key,
AppliedGPOListProvider* gpo_provider)
: AsyncPolicyLoader(task_runner),
is_initialized_(false),
chrome_policy_key_(chrome_policy_key),
gpo_provider_(gpo_provider),
user_policy_changed_event_(false, false),
machine_policy_changed_event_(false, false),
user_policy_watcher_failed_(false),
machine_policy_watcher_failed_(false) {
if (!::RegisterGPNotification(user_policy_changed_event_.handle(), false)) {
DPLOG(WARNING) << "Failed to register user group policy notification";
user_policy_watcher_failed_ = true;
}
if (!::RegisterGPNotification(machine_policy_changed_event_.handle(), true)) {
DPLOG(WARNING) << "Failed to register machine group policy notification.";
machine_policy_watcher_failed_ = true;
}
}
PolicyLoaderWin::~PolicyLoaderWin() {
if (!user_policy_watcher_failed_) {
::UnregisterGPNotification(user_policy_changed_event_.handle());
user_policy_watcher_.StopWatching();
}
if (!machine_policy_watcher_failed_) {
::UnregisterGPNotification(machine_policy_changed_event_.handle());
machine_policy_watcher_.StopWatching();
}
}
// static
std::unique_ptr<PolicyLoaderWin> PolicyLoaderWin::Create(
scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::string16& chrome_policy_key) {
return base::WrapUnique(new PolicyLoaderWin(
task_runner, chrome_policy_key, g_win_gpo_list_provider.Pointer()));
}
void PolicyLoaderWin::InitOnBackgroundThread() {
is_initialized_ = true;
SetupWatches();
CollectEnterpriseUMAs();
}
std::unique_ptr<PolicyBundle> PolicyLoaderWin::Load() {
// Reset the watches BEFORE reading the individual policies to avoid
// missing a change notification.
if (is_initialized_)
SetupWatches();
// Policy scope and corresponding hive.
static const struct {
PolicyScope scope;
HKEY hive;
} kScopes[] = {
{ POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE },
{ POLICY_SCOPE_USER, HKEY_CURRENT_USER },
};
bool is_enterprise = base::win::IsEnrolledToDomain();
VLOG(1) << "Reading policy from the registry is "
<< (is_enterprise ? "enabled." : "disabled.");
// Load policy data for the different scopes/levels and merge them.
std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
PolicyMap* chrome_policy =
&bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
for (size_t i = 0; i < arraysize(kScopes); ++i) {
PolicyScope scope = kScopes[i].scope;
PolicyLoadStatusSample status;
RegistryDict gpo_dict;
// Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and
// a matching LeaveCriticalPolicySection() call below after the
// ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be
// unavailable for extended periods of time, and there are reports of this
// happening in the wild: http://crbug.com/265862.
//
// Blocking for minutes is neither acceptable for Chrome startup, nor on
// the FILE thread on which this code runs in steady state. Given that
// there have never been any reports of issues due to partially-applied /
// corrupt group policy, this code intentionally omits the
// EnterCriticalPolicySection() call.
//
// If there's ever reason to revisit this decision, one option could be to
// make the EnterCriticalPolicySection() call on a dedicated thread and
// timeout on it more aggressively. For now, there's no justification for
// the additional effort this would introduce.
bool is_registry_forced = is_enterprise || gpo_provider_ == nullptr;
if (is_registry_forced || !ReadPolicyFromGPO(scope, &gpo_dict, &status)) {
VLOG_IF(1, !is_registry_forced) << "Failed to read GPO files for "
<< scope << " falling back to registry.";
gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_);
}
// Remove special-cased entries from the GPO dictionary.
std::unique_ptr<RegistryDict> recommended_dict(
gpo_dict.RemoveKey(kKeyRecommended));
std::unique_ptr<RegistryDict> third_party_dict(
gpo_dict.RemoveKey(kKeyThirdParty));
// Load Chrome policy.
LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy);
LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope,
chrome_policy);
// Load 3rd-party policy.
if (third_party_dict)
Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get());
}
return bundle;
}
bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file,
RegistryDict* policy,
PolicyLoadStatusSample* status) {
// The following deals with the minor annoyance that Wow64 FS redirection
// might need to be turned off: This is the case if running as a 32-bit
// process on a 64-bit system, in which case Wow64 FS redirection redirects
// access to the %WINDIR%/System32/GroupPolicy directory to
// %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the
// system-native directory.
if (base::PathExists(preg_file)) {
return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy, status);
} else {
// Try with redirection switched off.
ScopedDisableWow64Redirection redirection_disable;
if (redirection_disable.is_active() && base::PathExists(preg_file)) {
status->Add(POLICY_LOAD_STATUS_WOW64_REDIRECTION_DISABLED);
return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy,
status);
}
}
// Report the error.
LOG(ERROR) << "PReg file doesn't exist: " << preg_file.value();
status->Add(POLICY_LOAD_STATUS_MISSING);
return false;
}
bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope,
PGROUP_POLICY_OBJECT policy_object_list,
RegistryDict* policy,
PolicyLoadStatusSample* status) {
RegistryDict parsed_policy;
RegistryDict forced_policy;
for (GROUP_POLICY_OBJECT* policy_object = policy_object_list;
policy_object; policy_object = policy_object->pNext) {
if (policy_object->dwOptions & GPO_FLAG_DISABLE)
continue;
if (PathIsUNC(policy_object->lpFileSysPath)) {
// UNC path: Assume this is an AD-managed machine, which updates the
// registry via GPO's standard registry CSE periodically. Fall back to
// reading from the registry in this case.
status->Add(POLICY_LOAD_STATUS_INACCCESSIBLE);
return false;
}
base::FilePath preg_file_path(
base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName));
if (policy_object->dwOptions & GPO_FLAG_FORCE) {
RegistryDict new_forced_policy;
if (!ReadPRegFile(preg_file_path, &new_forced_policy, status))
return false;
// Merge with existing forced policy, giving precedence to the existing
// forced policy.
new_forced_policy.Merge(forced_policy);
forced_policy.Swap(&new_forced_policy);
} else {
if (!ReadPRegFile(preg_file_path, &parsed_policy, status))
return false;
}
}
// Merge, give precedence to forced policy.
parsed_policy.Merge(forced_policy);
policy->Swap(&parsed_policy);
return true;
}
bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope,
RegistryDict* policy,
PolicyLoadStatusSample* status) {
PGROUP_POLICY_OBJECT policy_object_list = NULL;
DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0;
if (gpo_provider_->GetAppliedGPOList(
flags, NULL, NULL, &kRegistrySettingsCSEGUID,
&policy_object_list) != ERROR_SUCCESS) {
PLOG(ERROR) << "GetAppliedGPOList scope " << scope;
status->Add(POLICY_LOAD_STATUS_QUERY_FAILED);
return false;
}
bool result = true;
if (policy_object_list) {
result = LoadGPOPolicy(scope, policy_object_list, policy, status);
if (!gpo_provider_->FreeGPOList(policy_object_list))
LOG(WARNING) << "FreeGPOList";
} else {
status->Add(POLICY_LOAD_STATUS_NO_POLICY);
}
return result;
}
void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict,
PolicyLevel level,
PolicyScope scope,
PolicyMap* chrome_policy_map) {
PolicyMap policy;
const Schema* chrome_schema =
schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
ParsePolicy(gpo_dict, level, scope, *chrome_schema, &policy);
FilterUntrustedPolicy(&policy);
chrome_policy_map->MergeFrom(policy);
}
void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict,
PolicyScope scope,
PolicyBundle* bundle) {
// Map of known 3rd party policy domain name to their enum values.
static const struct {
const char* name;
PolicyDomain domain;
} k3rdPartyDomains[] = {
{ "extensions", POLICY_DOMAIN_EXTENSIONS },
};
// Policy level and corresponding path.
static const struct {
PolicyLevel level;
const char* path;
} kLevels[] = {
{ POLICY_LEVEL_MANDATORY, kKeyMandatory },
{ POLICY_LEVEL_RECOMMENDED, kKeyRecommended },
};
for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) {
const char* name = k3rdPartyDomains[i].name;
const PolicyDomain domain = k3rdPartyDomains[i].domain;
const RegistryDict* domain_dict = gpo_dict->GetKey(name);
if (!domain_dict)
continue;
for (RegistryDict::KeyMap::const_iterator component(
domain_dict->keys().begin());
component != domain_dict->keys().end();
++component) {
const PolicyNamespace policy_namespace(domain, component->first);
const Schema* schema_from_map = schema_map()->GetSchema(policy_namespace);
if (!schema_from_map) {
// This extension isn't installed or doesn't support policies.
continue;
}
Schema schema = *schema_from_map;
if (!schema.valid() &&
policy_namespace.domain == POLICY_DOMAIN_EXTENSIONS &&
policy_namespace.component_id == kLegacyBrowserSupportExtensionId) {
// TODO(joaodasilva): remove this special treatment for LBS by M35.
std::string schema_json;
const base::Value* value = component->second->GetValue(kKeySchema);
if (value && value->GetAsString(&schema_json)) {
std::string error;
schema = Schema::Parse(PatchSchema(schema_json), &error);
if (!schema.valid())
LOG(WARNING) << "Invalid schema in the registry for LBS: " << error;
}
}
// Parse policy.
for (size_t j = 0; j < arraysize(kLevels); j++) {
const RegistryDict* policy_dict =
component->second->GetKey(kLevels[j].path);
if (!policy_dict)
continue;
PolicyMap policy;
ParsePolicy(policy_dict, kLevels[j].level, scope, schema, &policy);
bundle->Get(policy_namespace).MergeFrom(policy);
}
}
}
}
void PolicyLoaderWin::SetupWatches() {
DCHECK(is_initialized_);
if (!user_policy_watcher_failed_ &&
!user_policy_watcher_.GetWatchedObject() &&
!user_policy_watcher_.StartWatchingOnce(
user_policy_changed_event_.handle(), this)) {
DLOG(WARNING) << "Failed to start watch for user policy change event";
user_policy_watcher_failed_ = true;
}
if (!machine_policy_watcher_failed_ &&
!machine_policy_watcher_.GetWatchedObject() &&
!machine_policy_watcher_.StartWatchingOnce(
machine_policy_changed_event_.handle(), this)) {
DLOG(WARNING) << "Failed to start watch for machine policy change event";
machine_policy_watcher_failed_ = true;
}
}
void PolicyLoaderWin::OnObjectSignaled(HANDLE object) {
DCHECK(object == user_policy_changed_event_.handle() ||
object == machine_policy_changed_event_.handle())
<< "unexpected object signaled policy reload, obj = "
<< std::showbase << std::hex << object;
Reload(false);
}
} // namespace policy