1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/policy/core/common/policy_loader_win.h"
8 #include <lm.h> // For limits.
9 #include <ntdsapi.h> // For Ds[Un]Bind
10 #include <rpc.h> // For struct GUID
11 #include <shlwapi.h> // For PathIsUNC()
12 #include <userenv.h> // For GPO functions
17 // shlwapi.dll is required for PathIsUNC().
18 #pragma comment(lib, "shlwapi.lib")
19 // userenv.dll is required for various GPO functions.
20 #pragma comment(lib, "userenv.lib")
21 // ntdsapi.dll is required for Ds[Un]Bind calls.
22 #pragma comment(lib, "ntdsapi.lib")
24 #include "base/basictypes.h"
25 #include "base/bind.h"
26 #include "base/files/file_util.h"
27 #include "base/json/json_reader.h"
28 #include "base/json/json_writer.h"
29 #include "base/lazy_instance.h"
30 #include "base/logging.h"
31 #include "base/memory/scoped_ptr.h"
32 #include "base/metrics/histogram.h"
33 #include "base/metrics/sparse_histogram.h"
34 #include "base/scoped_native_library.h"
35 #include "base/sequenced_task_runner.h"
36 #include "base/stl_util.h"
37 #include "base/strings/string16.h"
38 #include "base/strings/string_util.h"
39 #include "base/values.h"
40 #include "base/win/win_util.h"
41 #include "base/win/windows_version.h"
42 #include "components/json_schema/json_schema_constants.h"
43 #include "components/policy/core/common/policy_bundle.h"
44 #include "components/policy/core/common/policy_load_status.h"
45 #include "components/policy/core/common/policy_map.h"
46 #include "components/policy/core/common/policy_namespace.h"
47 #include "components/policy/core/common/preg_parser_win.h"
48 #include "components/policy/core/common/registry_dict_win.h"
49 #include "components/policy/core/common/schema.h"
50 #include "policy/policy_constants.h"
52 namespace schema
= json_schema_constants
;
58 const char kKeyMandatory
[] = "policy";
59 const char kKeyRecommended
[] = "recommended";
60 const char kKeySchema
[] = "schema";
61 const char kKeyThirdParty
[] = "3rdparty";
63 // The Legacy Browser Support was the first user of the policy-for-extensions
64 // API, and relied on behavior that will be phased out. If this extension is
65 // present then its policies will be loaded in a special way.
66 // TODO(joaodasilva): remove this for M35. http://crbug.com/325349
67 const char kLegacyBrowserSupportExtensionId
[] =
68 "heildphpnddilhkemkielfhnkaagiabh";
70 // The web store url that is the only trusted source for extensions.
71 const char kExpectedWebStoreUrl
[] =
72 ";https://clients2.google.com/service/update2/crx";
73 // String to be prepended to each blocked entry.
74 const char kBlockedExtensionPrefix
[] = "[BLOCKED]";
76 // List of policies that are considered only if the user is part of a AD domain.
77 // Please document any new additions in policy_templates.json!
78 const char* kInsecurePolicies
[] = {
79 key::kMetricsReportingEnabled
,
80 key::kDefaultSearchProviderEnabled
,
81 key::kHomepageIsNewTabPage
,
82 key::kHomepageLocation
,
83 key::kRestoreOnStartup
,
84 key::kRestoreOnStartupURLs
87 // The GUID of the registry settings group policy extension.
88 GUID kRegistrySettingsCSEGUID
= REGISTRY_EXTENSION_GUID
;
90 // The list of possible errors that can occur while collecting information about
91 // the current enterprise environment.
92 // This enum is used to define the buckets for an enumerated UMA histogram.
94 // (a) existing enumerated constants should never be deleted or reordered, and
95 // (b) new constants should only be appended at the end of the enumeration.
96 enum DomainCheckErrors
{
97 DOMAIN_CHECK_ERROR_GET_JOIN_INFO
= 0,
98 DOMAIN_CHECK_ERROR_DS_BIND
= 1,
99 DOMAIN_CHECK_ERROR_SIZE
, // Not a DomainCheckError. Must be last.
102 // If the LBS extension is found and contains a schema in the registry then this
103 // function is used to patch it, and make it compliant. The fix is to
104 // add an "items" attribute to lists that don't declare it.
105 std::string
PatchSchema(const std::string
& schema
) {
106 base::JSONParserOptions options
= base::JSON_PARSE_RFC
;
107 scoped_ptr
<base::Value
> json(base::JSONReader::Read(schema
, options
));
108 base::DictionaryValue
* dict
= NULL
;
109 base::DictionaryValue
* properties
= NULL
;
111 !json
->GetAsDictionary(&dict
) ||
112 !dict
->GetDictionary(schema::kProperties
, &properties
)) {
116 for (base::DictionaryValue::Iterator
it(*properties
);
117 !it
.IsAtEnd(); it
.Advance()) {
118 base::DictionaryValue
* policy_schema
= NULL
;
120 if (properties
->GetDictionary(it
.key(), &policy_schema
) &&
121 policy_schema
->GetString(schema::kType
, &type
) &&
122 type
== schema::kArray
&&
123 !policy_schema
->HasKey(schema::kItems
)) {
124 scoped_ptr
<base::DictionaryValue
> items(new base::DictionaryValue());
125 items
->SetString(schema::kType
, schema::kString
);
126 policy_schema
->Set(schema::kItems
, items
.release());
130 std::string serialized
;
131 base::JSONWriter::Write(json
.get(), &serialized
);
135 // Verifies that untrusted policies contain only safe values. Modifies the
136 // |policy| in place.
137 void FilterUntrustedPolicy(PolicyMap
* policy
) {
138 if (base::win::IsEnrolledToDomain())
141 int invalid_policies
= 0;
142 const PolicyMap::Entry
* map_entry
=
143 policy
->Get(key::kExtensionInstallForcelist
);
144 if (map_entry
&& map_entry
->value
) {
145 const base::ListValue
* policy_list_value
= NULL
;
146 if (!map_entry
->value
->GetAsList(&policy_list_value
))
149 scoped_ptr
<base::ListValue
> filtered_values(new base::ListValue
);
150 for (base::ListValue::const_iterator
list_entry(policy_list_value
->begin());
151 list_entry
!= policy_list_value
->end(); ++list_entry
) {
153 if (!(*list_entry
)->GetAsString(&entry
))
155 size_t pos
= entry
.find(';');
156 if (pos
== std::string::npos
)
158 // Only allow custom update urls in enterprise environments.
159 if (!LowerCaseEqualsASCII(entry
.substr(pos
), kExpectedWebStoreUrl
)) {
160 entry
= kBlockedExtensionPrefix
+ entry
;
164 filtered_values
->AppendString(entry
);
166 if (invalid_policies
) {
167 policy
->Set(key::kExtensionInstallForcelist
,
168 map_entry
->level
, map_entry
->scope
,
169 filtered_values
.release(),
170 map_entry
->external_data_fetcher
);
172 const PolicyDetails
* details
= GetChromePolicyDetails(
173 key::kExtensionInstallForcelist
);
174 UMA_HISTOGRAM_SPARSE_SLOWLY("EnterpriseCheck.InvalidPolicies",
179 for (size_t i
= 0; i
< arraysize(kInsecurePolicies
); ++i
) {
180 if (policy
->Get(kInsecurePolicies
[i
])) {
181 // TODO(pastarmovj): Surface this issue in the about:policy page.
182 policy
->Erase(kInsecurePolicies
[i
]);
184 const PolicyDetails
* details
=
185 GetChromePolicyDetails(kInsecurePolicies
[i
]);
186 UMA_HISTOGRAM_SPARSE_SLOWLY("EnterpriseCheck.InvalidPolicies",
191 UMA_HISTOGRAM_COUNTS("EnterpriseCheck.InvalidPoliciesDetected",
195 // A helper class encapsulating run-time-linked function calls to Wow64 APIs.
196 class Wow64Functions
{
199 : kernel32_lib_(base::FilePath(L
"kernel32")),
200 is_wow_64_process_(NULL
),
201 wow_64_disable_wow_64_fs_redirection_(NULL
),
202 wow_64_revert_wow_64_fs_redirection_(NULL
) {
203 if (kernel32_lib_
.is_valid()) {
204 is_wow_64_process_
= reinterpret_cast<IsWow64Process
>(
205 kernel32_lib_
.GetFunctionPointer("IsWow64Process"));
206 wow_64_disable_wow_64_fs_redirection_
=
207 reinterpret_cast<Wow64DisableWow64FSRedirection
>(
208 kernel32_lib_
.GetFunctionPointer(
209 "Wow64DisableWow64FsRedirection"));
210 wow_64_revert_wow_64_fs_redirection_
=
211 reinterpret_cast<Wow64RevertWow64FSRedirection
>(
212 kernel32_lib_
.GetFunctionPointer(
213 "Wow64RevertWow64FsRedirection"));
218 return is_wow_64_process_
&&
219 wow_64_disable_wow_64_fs_redirection_
&&
220 wow_64_revert_wow_64_fs_redirection_
;
225 if (!is_wow_64_process_(GetCurrentProcess(), &result
))
226 PLOG(WARNING
) << "IsWow64ProcFailed";
230 bool DisableFsRedirection(PVOID
* previous_state
) {
231 return !!wow_64_disable_wow_64_fs_redirection_(previous_state
);
234 bool RevertFsRedirection(PVOID previous_state
) {
235 return !!wow_64_revert_wow_64_fs_redirection_(previous_state
);
239 typedef BOOL (WINAPI
* IsWow64Process
)(HANDLE
, PBOOL
);
240 typedef BOOL (WINAPI
* Wow64DisableWow64FSRedirection
)(PVOID
*);
241 typedef BOOL (WINAPI
* Wow64RevertWow64FSRedirection
)(PVOID
);
243 base::ScopedNativeLibrary kernel32_lib_
;
245 IsWow64Process is_wow_64_process_
;
246 Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_
;
247 Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_
;
249 DISALLOW_COPY_AND_ASSIGN(Wow64Functions
);
252 // Global Wow64Function instance used by ScopedDisableWow64Redirection below.
253 static base::LazyInstance
<Wow64Functions
> g_wow_64_functions
=
254 LAZY_INSTANCE_INITIALIZER
;
256 // Scoper that switches off Wow64 File System Redirection during its lifetime.
257 class ScopedDisableWow64Redirection
{
259 ScopedDisableWow64Redirection()
261 previous_state_(NULL
) {
262 Wow64Functions
* wow64
= g_wow_64_functions
.Pointer();
263 if (wow64
->is_valid() && wow64
->IsWow64()) {
264 if (wow64
->DisableFsRedirection(&previous_state_
))
267 PLOG(WARNING
) << "Wow64DisableWow64FSRedirection";
271 ~ScopedDisableWow64Redirection() {
273 CHECK(g_wow_64_functions
.Get().RevertFsRedirection(previous_state_
));
276 bool is_active() { return active_
; }
280 PVOID previous_state_
;
282 DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection
);
285 // AppliedGPOListProvider implementation that calls actual Windows APIs.
286 class WinGPOListProvider
: public AppliedGPOListProvider
{
288 virtual ~WinGPOListProvider() {}
290 // AppliedGPOListProvider:
291 virtual DWORD
GetAppliedGPOList(DWORD flags
,
292 LPCTSTR machine_name
,
294 GUID
* extension_guid
,
295 PGROUP_POLICY_OBJECT
* gpo_list
) override
{
296 return ::GetAppliedGPOList(flags
, machine_name
, sid_user
, extension_guid
,
300 virtual BOOL
FreeGPOList(PGROUP_POLICY_OBJECT gpo_list
) override
{
301 return ::FreeGPOList(gpo_list
);
305 // The default windows GPO list provider used for PolicyLoaderWin.
306 static base::LazyInstance
<WinGPOListProvider
> g_win_gpo_list_provider
=
307 LAZY_INSTANCE_INITIALIZER
;
309 // Parses |gpo_dict| according to |schema| and writes the resulting policy
310 // settings to |policy| for the given |scope| and |level|.
311 void ParsePolicy(const RegistryDict
* gpo_dict
,
314 const Schema
& schema
,
319 scoped_ptr
<base::Value
> policy_value(gpo_dict
->ConvertToJSON(schema
));
320 const base::DictionaryValue
* policy_dict
= NULL
;
321 if (!policy_value
->GetAsDictionary(&policy_dict
) || !policy_dict
) {
322 LOG(WARNING
) << "Root policy object is not a dictionary!";
326 policy
->LoadFrom(policy_dict
, level
, scope
);
329 // Collects stats about the enterprise environment that can be used to decide
330 // how to parse the existing policy information.
331 void CollectEnterpriseUMAs() {
332 // Collect statistics about the windows suite.
333 UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.OSType",
334 base::win::OSInfo::GetInstance()->version_type(),
335 base::win::SUITE_LAST
);
337 // Get the computer's domain status.
339 NETSETUP_JOIN_STATUS join_status
;
340 if (NERR_Success
!= ::NetGetJoinInformation(NULL
, &domain
, &join_status
)) {
341 UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed",
342 DOMAIN_CHECK_ERROR_GET_JOIN_INFO
,
343 DOMAIN_CHECK_ERROR_SIZE
);
346 ::NetApiBufferFree(domain
);
348 bool in_domain
= join_status
== NetSetupDomainName
;
349 UMA_HISTOGRAM_BOOLEAN("EnterpriseCheck.InDomain", in_domain
);
351 // This check will tell us how often are domain computers actually
352 // connected to the enterprise network while Chrome is running.
354 if (ERROR_SUCCESS
== ::DsBind(NULL
, NULL
, &server_bind
)) {
355 UMA_HISTOGRAM_COUNTS("EnterpriseCheck.DomainBindSucceeded", 1);
356 ::DsUnBind(&server_bind
);
358 UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed",
359 DOMAIN_CHECK_ERROR_DS_BIND
,
360 DOMAIN_CHECK_ERROR_SIZE
);
367 const base::FilePath::CharType
PolicyLoaderWin::kPRegFileName
[] =
368 FILE_PATH_LITERAL("Registry.pol");
370 PolicyLoaderWin::PolicyLoaderWin(
371 scoped_refptr
<base::SequencedTaskRunner
> task_runner
,
372 const base::string16
& chrome_policy_key
,
373 AppliedGPOListProvider
* gpo_provider
)
374 : AsyncPolicyLoader(task_runner
),
375 is_initialized_(false),
376 chrome_policy_key_(chrome_policy_key
),
377 gpo_provider_(gpo_provider
),
378 user_policy_changed_event_(false, false),
379 machine_policy_changed_event_(false, false),
380 user_policy_watcher_failed_(false),
381 machine_policy_watcher_failed_(false) {
382 if (!::RegisterGPNotification(user_policy_changed_event_
.handle(), false)) {
383 DPLOG(WARNING
) << "Failed to register user group policy notification";
384 user_policy_watcher_failed_
= true;
386 if (!::RegisterGPNotification(machine_policy_changed_event_
.handle(), true)) {
387 DPLOG(WARNING
) << "Failed to register machine group policy notification.";
388 machine_policy_watcher_failed_
= true;
392 PolicyLoaderWin::~PolicyLoaderWin() {
393 if (!user_policy_watcher_failed_
) {
394 ::UnregisterGPNotification(user_policy_changed_event_
.handle());
395 user_policy_watcher_
.StopWatching();
397 if (!machine_policy_watcher_failed_
) {
398 ::UnregisterGPNotification(machine_policy_changed_event_
.handle());
399 machine_policy_watcher_
.StopWatching();
404 scoped_ptr
<PolicyLoaderWin
> PolicyLoaderWin::Create(
405 scoped_refptr
<base::SequencedTaskRunner
> task_runner
,
406 const base::string16
& chrome_policy_key
) {
407 return make_scoped_ptr(
408 new PolicyLoaderWin(task_runner
,
410 g_win_gpo_list_provider
.Pointer()));
413 void PolicyLoaderWin::InitOnBackgroundThread() {
414 is_initialized_
= true;
416 CollectEnterpriseUMAs();
419 scoped_ptr
<PolicyBundle
> PolicyLoaderWin::Load() {
420 // Reset the watches BEFORE reading the individual policies to avoid
421 // missing a change notification.
425 // Policy scope and corresponding hive.
426 static const struct {
430 { POLICY_SCOPE_MACHINE
, HKEY_LOCAL_MACHINE
},
431 { POLICY_SCOPE_USER
, HKEY_CURRENT_USER
},
434 bool is_enterprise
= base::win::IsEnrolledToDomain();
435 VLOG(1) << "Reading policy from the registry is "
436 << (is_enterprise
? "enabled." : "disabled.");
438 // Load policy data for the different scopes/levels and merge them.
439 scoped_ptr
<PolicyBundle
> bundle(new PolicyBundle());
440 PolicyMap
* chrome_policy
=
441 &bundle
->Get(PolicyNamespace(POLICY_DOMAIN_CHROME
, std::string()));
442 for (size_t i
= 0; i
< arraysize(kScopes
); ++i
) {
443 PolicyScope scope
= kScopes
[i
].scope
;
444 PolicyLoadStatusSample status
;
445 RegistryDict gpo_dict
;
447 // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and
448 // a matching LeaveCriticalPolicySection() call below after the
449 // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be
450 // unavailable for extended periods of time, and there are reports of this
451 // happening in the wild: http://crbug.com/265862.
453 // Blocking for minutes is neither acceptable for Chrome startup, nor on
454 // the FILE thread on which this code runs in steady state. Given that
455 // there have never been any reports of issues due to partially-applied /
456 // corrupt group policy, this code intentionally omits the
457 // EnterCriticalPolicySection() call.
459 // If there's ever reason to revisit this decision, one option could be to
460 // make the EnterCriticalPolicySection() call on a dedicated thread and
461 // timeout on it more aggressively. For now, there's no justification for
462 // the additional effort this would introduce.
464 bool is_registry_forced
= is_enterprise
|| gpo_provider_
== nullptr;
465 if (is_registry_forced
|| !ReadPolicyFromGPO(scope
, &gpo_dict
, &status
)) {
466 VLOG_IF(1, !is_registry_forced
) << "Failed to read GPO files for "
467 << scope
<< " falling back to registry.";
468 gpo_dict
.ReadRegistry(kScopes
[i
].hive
, chrome_policy_key_
);
471 // Remove special-cased entries from the GPO dictionary.
472 scoped_ptr
<RegistryDict
> recommended_dict(
473 gpo_dict
.RemoveKey(kKeyRecommended
));
474 scoped_ptr
<RegistryDict
> third_party_dict(
475 gpo_dict
.RemoveKey(kKeyThirdParty
));
477 // Load Chrome policy.
478 LoadChromePolicy(&gpo_dict
, POLICY_LEVEL_MANDATORY
, scope
, chrome_policy
);
479 LoadChromePolicy(recommended_dict
.get(), POLICY_LEVEL_RECOMMENDED
, scope
,
482 // Load 3rd-party policy.
483 if (third_party_dict
)
484 Load3rdPartyPolicy(third_party_dict
.get(), scope
, bundle
.get());
487 return bundle
.Pass();
490 bool PolicyLoaderWin::ReadPRegFile(const base::FilePath
& preg_file
,
491 RegistryDict
* policy
,
492 PolicyLoadStatusSample
* status
) {
493 // The following deals with the minor annoyance that Wow64 FS redirection
494 // might need to be turned off: This is the case if running as a 32-bit
495 // process on a 64-bit system, in which case Wow64 FS redirection redirects
496 // access to the %WINDIR%/System32/GroupPolicy directory to
497 // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the
498 // system-native directory.
499 if (base::PathExists(preg_file
)) {
500 return preg_parser::ReadFile(preg_file
, chrome_policy_key_
, policy
, status
);
502 // Try with redirection switched off.
503 ScopedDisableWow64Redirection redirection_disable
;
504 if (redirection_disable
.is_active() && base::PathExists(preg_file
)) {
505 status
->Add(POLICY_LOAD_STATUS_WOW64_REDIRECTION_DISABLED
);
506 return preg_parser::ReadFile(preg_file
, chrome_policy_key_
, policy
,
512 LOG(ERROR
) << "PReg file doesn't exist: " << preg_file
.value();
513 status
->Add(POLICY_LOAD_STATUS_MISSING
);
517 bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope
,
518 PGROUP_POLICY_OBJECT policy_object_list
,
519 RegistryDict
* policy
,
520 PolicyLoadStatusSample
* status
) {
521 RegistryDict parsed_policy
;
522 RegistryDict forced_policy
;
523 for (GROUP_POLICY_OBJECT
* policy_object
= policy_object_list
;
524 policy_object
; policy_object
= policy_object
->pNext
) {
525 if (policy_object
->dwOptions
& GPO_FLAG_DISABLE
)
528 if (PathIsUNC(policy_object
->lpFileSysPath
)) {
529 // UNC path: Assume this is an AD-managed machine, which updates the
530 // registry via GPO's standard registry CSE periodically. Fall back to
531 // reading from the registry in this case.
532 status
->Add(POLICY_LOAD_STATUS_INACCCESSIBLE
);
536 base::FilePath
preg_file_path(
537 base::FilePath(policy_object
->lpFileSysPath
).Append(kPRegFileName
));
538 if (policy_object
->dwOptions
& GPO_FLAG_FORCE
) {
539 RegistryDict new_forced_policy
;
540 if (!ReadPRegFile(preg_file_path
, &new_forced_policy
, status
))
543 // Merge with existing forced policy, giving precedence to the existing
545 new_forced_policy
.Merge(forced_policy
);
546 forced_policy
.Swap(&new_forced_policy
);
548 if (!ReadPRegFile(preg_file_path
, &parsed_policy
, status
))
553 // Merge, give precedence to forced policy.
554 parsed_policy
.Merge(forced_policy
);
555 policy
->Swap(&parsed_policy
);
560 bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope
,
561 RegistryDict
* policy
,
562 PolicyLoadStatusSample
* status
) {
563 PGROUP_POLICY_OBJECT policy_object_list
= NULL
;
564 DWORD flags
= scope
== POLICY_SCOPE_MACHINE
? GPO_LIST_FLAG_MACHINE
: 0;
565 if (gpo_provider_
->GetAppliedGPOList(
566 flags
, NULL
, NULL
, &kRegistrySettingsCSEGUID
,
567 &policy_object_list
) != ERROR_SUCCESS
) {
568 PLOG(ERROR
) << "GetAppliedGPOList scope " << scope
;
569 status
->Add(POLICY_LOAD_STATUS_QUERY_FAILED
);
574 if (policy_object_list
) {
575 result
= LoadGPOPolicy(scope
, policy_object_list
, policy
, status
);
576 if (!gpo_provider_
->FreeGPOList(policy_object_list
))
577 LOG(WARNING
) << "FreeGPOList";
579 status
->Add(POLICY_LOAD_STATUS_NO_POLICY
);
585 void PolicyLoaderWin::LoadChromePolicy(const RegistryDict
* gpo_dict
,
588 PolicyMap
* chrome_policy_map
) {
590 const Schema
* chrome_schema
=
591 schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME
, ""));
592 ParsePolicy(gpo_dict
, level
, scope
, *chrome_schema
, &policy
);
593 FilterUntrustedPolicy(&policy
);
594 chrome_policy_map
->MergeFrom(policy
);
597 void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict
* gpo_dict
,
599 PolicyBundle
* bundle
) {
600 // Map of known 3rd party policy domain name to their enum values.
601 static const struct {
604 } k3rdPartyDomains
[] = {
605 { "extensions", POLICY_DOMAIN_EXTENSIONS
},
608 // Policy level and corresponding path.
609 static const struct {
613 { POLICY_LEVEL_MANDATORY
, kKeyMandatory
},
614 { POLICY_LEVEL_RECOMMENDED
, kKeyRecommended
},
617 for (size_t i
= 0; i
< arraysize(k3rdPartyDomains
); i
++) {
618 const char* name
= k3rdPartyDomains
[i
].name
;
619 const PolicyDomain domain
= k3rdPartyDomains
[i
].domain
;
620 const RegistryDict
* domain_dict
= gpo_dict
->GetKey(name
);
624 for (RegistryDict::KeyMap::const_iterator
component(
625 domain_dict
->keys().begin());
626 component
!= domain_dict
->keys().end();
628 const PolicyNamespace
policy_namespace(domain
, component
->first
);
630 const Schema
* schema_from_map
= schema_map()->GetSchema(policy_namespace
);
631 if (!schema_from_map
) {
632 // This extension isn't installed or doesn't support policies.
635 Schema schema
= *schema_from_map
;
637 if (!schema
.valid() &&
638 policy_namespace
.domain
== POLICY_DOMAIN_EXTENSIONS
&&
639 policy_namespace
.component_id
== kLegacyBrowserSupportExtensionId
) {
640 // TODO(joaodasilva): remove this special treatment for LBS by M35.
641 std::string schema_json
;
642 const base::Value
* value
= component
->second
->GetValue(kKeySchema
);
643 if (value
&& value
->GetAsString(&schema_json
)) {
645 schema
= Schema::Parse(PatchSchema(schema_json
), &error
);
647 LOG(WARNING
) << "Invalid schema in the registry for LBS: " << error
;
652 for (size_t j
= 0; j
< arraysize(kLevels
); j
++) {
653 const RegistryDict
* policy_dict
=
654 component
->second
->GetKey(kLevels
[j
].path
);
659 ParsePolicy(policy_dict
, kLevels
[j
].level
, scope
, schema
, &policy
);
660 bundle
->Get(policy_namespace
).MergeFrom(policy
);
666 void PolicyLoaderWin::SetupWatches() {
667 DCHECK(is_initialized_
);
668 if (!user_policy_watcher_failed_
&&
669 !user_policy_watcher_
.GetWatchedObject() &&
670 !user_policy_watcher_
.StartWatching(
671 user_policy_changed_event_
.handle(), this)) {
672 DLOG(WARNING
) << "Failed to start watch for user policy change event";
673 user_policy_watcher_failed_
= true;
675 if (!machine_policy_watcher_failed_
&&
676 !machine_policy_watcher_
.GetWatchedObject() &&
677 !machine_policy_watcher_
.StartWatching(
678 machine_policy_changed_event_
.handle(), this)) {
679 DLOG(WARNING
) << "Failed to start watch for machine policy change event";
680 machine_policy_watcher_failed_
= true;
684 void PolicyLoaderWin::OnObjectSignaled(HANDLE object
) {
685 DCHECK(object
== user_policy_changed_event_
.handle() ||
686 object
== machine_policy_changed_event_
.handle())
687 << "unexpected object signaled policy reload, obj = "
688 << std::showbase
<< std::hex
<< object
;
692 } // namespace policy