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. 4 5 #include "components/policy/core/common/policy_loader_win.h" 6 7 #include <windows.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 13 14 #include <string> 15 #include <vector> 16 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") 23 24 #include "base/basictypes.h" 25 #include "base/bind.h" 26 #include "base/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/scoped_native_library.h" 34 #include "base/sequenced_task_runner.h" 35 #include "base/stl_util.h" 36 #include "base/strings/string16.h" 37 #include "base/strings/string_util.h" 38 #include "base/values.h" 39 #include "base/win/win_util.h" 40 #include "base/win/windows_version.h" 41 #include "components/json_schema/json_schema_constants.h" 42 #include "components/policy/core/common/policy_bundle.h" 43 #include "components/policy/core/common/policy_load_status.h" 44 #include "components/policy/core/common/policy_map.h" 45 #include "components/policy/core/common/policy_namespace.h" 46 #include "components/policy/core/common/preg_parser_win.h" 47 #include "components/policy/core/common/registry_dict_win.h" 48 #include "components/policy/core/common/schema.h" 49 #include "policy/policy_constants.h" 50 51 namespace schema = json_schema_constants; 52 53 namespace policy { 54 55 namespace { 56 57 const char kKeyMandatory[] = "policy"; 58 const char kKeyRecommended[] = "recommended"; 59 const char kKeySchema[] = "schema"; 60 const char kKeyThirdParty[] = "3rdparty"; 61 62 // The Legacy Browser Support was the first user of the policy-for-extensions 63 // API, and relied on behavior that will be phased out. If this extension is 64 // present then its policies will be loaded in a special way. 65 // TODO(joaodasilva): remove this for M35. http://crbug.com/325349 66 const char kLegacyBrowserSupportExtensionId[] = 67 "heildphpnddilhkemkielfhnkaagiabh"; 68 69 // The web store url that is the only trusted source for extensions. 70 const char kExpectedWebStoreUrl[] = 71 ";https://clients2.google.com/service/update2/crx"; 72 // String to be prepended to each blocked entry. 73 const char kBlockedExtensionPrefix[] = "[BLOCKED]"; 74 75 // The GUID of the registry settings group policy extension. 76 GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID; 77 78 // The list of possible errors that can occur while collecting information about 79 // the current enterprise environment. 80 enum DomainCheckErrors { 81 DOMAIN_CHECK_ERROR_GET_JOIN_INFO = 0, 82 DOMAIN_CHECK_ERROR_DS_BIND, 83 DOMAIN_CHECK_ERROR_LAST, 84 }; 85 86 // If the LBS extension is found and contains a schema in the registry then this 87 // function is used to patch it, and make it compliant. The fix is to 88 // add an "items" attribute to lists that don't declare it. 89 std::string PatchSchema(const std::string& schema) { 90 base::JSONParserOptions options = base::JSON_PARSE_RFC; 91 scoped_ptr<base::Value> json(base::JSONReader::Read(schema, options)); 92 base::DictionaryValue* dict = NULL; 93 base::DictionaryValue* properties = NULL; 94 if (!json || 95 !json->GetAsDictionary(&dict) || 96 !dict->GetDictionary(schema::kProperties, &properties)) { 97 return schema; 98 } 99 100 for (base::DictionaryValue::Iterator it(*properties); 101 !it.IsAtEnd(); it.Advance()) { 102 base::DictionaryValue* policy_schema = NULL; 103 std::string type; 104 if (properties->GetDictionary(it.key(), &policy_schema) && 105 policy_schema->GetString(schema::kType, &type) && 106 type == schema::kArray && 107 !policy_schema->HasKey(schema::kItems)) { 108 scoped_ptr<base::DictionaryValue> items(new base::DictionaryValue()); 109 items->SetString(schema::kType, schema::kString); 110 policy_schema->Set(schema::kItems, items.release()); 111 } 112 } 113 114 std::string serialized; 115 base::JSONWriter::Write(json.get(), &serialized); 116 return serialized; 117 } 118 119 // Verifies that untrusted policies contain only safe values. Modifies the 120 // |policy| in place. 121 void FilterUntrustedPolicy(PolicyMap* policy) { 122 if (base::win::IsEnrolledToDomain()) 123 return; 124 125 const PolicyMap::Entry* map_entry = 126 policy->Get(policy::key::kExtensionInstallForcelist); 127 if (map_entry && map_entry->value) { 128 int invalid_policies = 0; 129 const base::ListValue* policy_list_value = NULL; 130 if (!map_entry->value->GetAsList(&policy_list_value)) 131 return; 132 133 scoped_ptr<base::ListValue> filtered_values(new base::ListValue); 134 for (base::ListValue::const_iterator list_entry(policy_list_value->begin()); 135 list_entry != policy_list_value->end(); ++list_entry) { 136 std::string entry; 137 if (!(*list_entry)->GetAsString(&entry)) 138 continue; 139 size_t pos = entry.find(';'); 140 if (pos == std::string::npos) 141 continue; 142 // Only allow custom update urls in enterprise environments. 143 if (!LowerCaseEqualsASCII(entry.substr(pos), kExpectedWebStoreUrl)) { 144 entry = kBlockedExtensionPrefix + entry; 145 invalid_policies++; 146 } 147 148 filtered_values->AppendString(entry); 149 } 150 policy->Set(policy::key::kExtensionInstallForcelist, 151 map_entry->level, map_entry->scope, 152 filtered_values.release(), 153 map_entry->external_data_fetcher); 154 UMA_HISTOGRAM_COUNTS("EnterpriseCheck.InvalidPoliciesDetected", 155 invalid_policies); 156 } 157 } 158 159 // A helper class encapsulating run-time-linked function calls to Wow64 APIs. 160 class Wow64Functions { 161 public: 162 Wow64Functions() 163 : kernel32_lib_(base::FilePath(L"kernel32")), 164 is_wow_64_process_(NULL), 165 wow_64_disable_wow_64_fs_redirection_(NULL), 166 wow_64_revert_wow_64_fs_redirection_(NULL) { 167 if (kernel32_lib_.is_valid()) { 168 is_wow_64_process_ = reinterpret_cast<IsWow64Process>( 169 kernel32_lib_.GetFunctionPointer("IsWow64Process")); 170 wow_64_disable_wow_64_fs_redirection_ = 171 reinterpret_cast<Wow64DisableWow64FSRedirection>( 172 kernel32_lib_.GetFunctionPointer( 173 "Wow64DisableWow64FsRedirection")); 174 wow_64_revert_wow_64_fs_redirection_ = 175 reinterpret_cast<Wow64RevertWow64FSRedirection>( 176 kernel32_lib_.GetFunctionPointer( 177 "Wow64RevertWow64FsRedirection")); 178 } 179 } 180 181 bool is_valid() { 182 return is_wow_64_process_ && 183 wow_64_disable_wow_64_fs_redirection_ && 184 wow_64_revert_wow_64_fs_redirection_; 185 } 186 187 bool IsWow64() { 188 BOOL result = 0; 189 if (!is_wow_64_process_(GetCurrentProcess(), &result)) 190 PLOG(WARNING) << "IsWow64ProcFailed"; 191 return !!result; 192 } 193 194 bool DisableFsRedirection(PVOID* previous_state) { 195 return !!wow_64_disable_wow_64_fs_redirection_(previous_state); 196 } 197 198 bool RevertFsRedirection(PVOID previous_state) { 199 return !!wow_64_revert_wow_64_fs_redirection_(previous_state); 200 } 201 202 private: 203 typedef BOOL (WINAPI* IsWow64Process)(HANDLE, PBOOL); 204 typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*); 205 typedef BOOL (WINAPI* Wow64RevertWow64FSRedirection)(PVOID); 206 207 base::ScopedNativeLibrary kernel32_lib_; 208 209 IsWow64Process is_wow_64_process_; 210 Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_; 211 Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_; 212 213 DISALLOW_COPY_AND_ASSIGN(Wow64Functions); 214 }; 215 216 // Global Wow64Function instance used by ScopedDisableWow64Redirection below. 217 static base::LazyInstance<Wow64Functions> g_wow_64_functions = 218 LAZY_INSTANCE_INITIALIZER; 219 220 // Scoper that switches off Wow64 File System Redirection during its lifetime. 221 class ScopedDisableWow64Redirection { 222 public: 223 ScopedDisableWow64Redirection() 224 : active_(false), 225 previous_state_(NULL) { 226 Wow64Functions* wow64 = g_wow_64_functions.Pointer(); 227 if (wow64->is_valid() && wow64->IsWow64()) { 228 if (wow64->DisableFsRedirection(&previous_state_)) 229 active_ = true; 230 else 231 PLOG(WARNING) << "Wow64DisableWow64FSRedirection"; 232 } 233 } 234 235 ~ScopedDisableWow64Redirection() { 236 if (active_) 237 CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_)); 238 } 239 240 bool is_active() { return active_; } 241 242 private: 243 bool active_; 244 PVOID previous_state_; 245 246 DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection); 247 }; 248 249 // AppliedGPOListProvider implementation that calls actual Windows APIs. 250 class WinGPOListProvider : public AppliedGPOListProvider { 251 public: 252 virtual ~WinGPOListProvider() {} 253 254 // AppliedGPOListProvider: 255 virtual DWORD GetAppliedGPOList(DWORD flags, 256 LPCTSTR machine_name, 257 PSID sid_user, 258 GUID* extension_guid, 259 PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE { 260 return ::GetAppliedGPOList(flags, machine_name, sid_user, extension_guid, 261 gpo_list); 262 } 263 264 virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE { 265 return ::FreeGPOList(gpo_list); 266 } 267 }; 268 269 // The default windows GPO list provider used for PolicyLoaderWin. 270 static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider = 271 LAZY_INSTANCE_INITIALIZER; 272 273 // Parses |gpo_dict| according to |schema| and writes the resulting policy 274 // settings to |policy| for the given |scope| and |level|. 275 void ParsePolicy(const RegistryDict* gpo_dict, 276 PolicyLevel level, 277 PolicyScope scope, 278 const Schema& schema, 279 PolicyMap* policy) { 280 if (!gpo_dict) 281 return; 282 283 scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema)); 284 const base::DictionaryValue* policy_dict = NULL; 285 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { 286 LOG(WARNING) << "Root policy object is not a dictionary!"; 287 return; 288 } 289 290 policy->LoadFrom(policy_dict, level, scope); 291 } 292 293 // Collects stats about the enterprise environment that can be used to decide 294 // how to parse the existing policy information. 295 void CollectEnterpriseUMAs() { 296 // Collect statistics about the windows suite. 297 UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.OSType", 298 base::win::OSInfo::GetInstance()->version_type(), 299 base::win::SUITE_LAST); 300 301 // Get the computer's domain status. 302 LPWSTR domain; 303 NETSETUP_JOIN_STATUS join_status; 304 if (NERR_Success != ::NetGetJoinInformation(NULL, &domain, &join_status)) { 305 UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed", 306 DOMAIN_CHECK_ERROR_GET_JOIN_INFO, 307 DOMAIN_CHECK_ERROR_LAST); 308 return; 309 } 310 ::NetApiBufferFree(domain); 311 312 bool in_domain = join_status == NetSetupDomainName; 313 UMA_HISTOGRAM_BOOLEAN("EnterpriseCheck.InDomain", in_domain); 314 if (in_domain) { 315 // This check will tell us how often are domain computers actually 316 // connected to the enterprise network while Chrome is running. 317 HANDLE server_bind; 318 if (ERROR_SUCCESS == ::DsBind(NULL, NULL, &server_bind)) { 319 UMA_HISTOGRAM_COUNTS("EnterpriseCheck.DomainBindSucceeded", 1); 320 ::DsUnBind(&server_bind); 321 } else { 322 UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed", 323 DOMAIN_CHECK_ERROR_DS_BIND, 324 DOMAIN_CHECK_ERROR_LAST); 325 } 326 } 327 } 328 329 } // namespace 330 331 const base::FilePath::CharType PolicyLoaderWin::kPRegFileName[] = 332 FILE_PATH_LITERAL("Registry.pol"); 333 334 PolicyLoaderWin::PolicyLoaderWin( 335 scoped_refptr<base::SequencedTaskRunner> task_runner, 336 const base::string16& chrome_policy_key, 337 AppliedGPOListProvider* gpo_provider) 338 : AsyncPolicyLoader(task_runner), 339 is_initialized_(false), 340 chrome_policy_key_(chrome_policy_key), 341 gpo_provider_(gpo_provider), 342 user_policy_changed_event_(false, false), 343 machine_policy_changed_event_(false, false), 344 user_policy_watcher_failed_(false), 345 machine_policy_watcher_failed_(false) { 346 if (!::RegisterGPNotification(user_policy_changed_event_.handle(), false)) { 347 DPLOG(WARNING) << "Failed to register user group policy notification"; 348 user_policy_watcher_failed_ = true; 349 } 350 if (!::RegisterGPNotification(machine_policy_changed_event_.handle(), true)) { 351 DPLOG(WARNING) << "Failed to register machine group policy notification."; 352 machine_policy_watcher_failed_ = true; 353 } 354 } 355 356 PolicyLoaderWin::~PolicyLoaderWin() { 357 if (!user_policy_watcher_failed_) { 358 ::UnregisterGPNotification(user_policy_changed_event_.handle()); 359 user_policy_watcher_.StopWatching(); 360 } 361 if (!machine_policy_watcher_failed_) { 362 ::UnregisterGPNotification(machine_policy_changed_event_.handle()); 363 machine_policy_watcher_.StopWatching(); 364 } 365 } 366 367 // static 368 scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create( 369 scoped_refptr<base::SequencedTaskRunner> task_runner, 370 const base::string16& chrome_policy_key) { 371 return make_scoped_ptr( 372 new PolicyLoaderWin(task_runner, 373 chrome_policy_key, 374 g_win_gpo_list_provider.Pointer())); 375 } 376 377 void PolicyLoaderWin::InitOnBackgroundThread() { 378 is_initialized_ = true; 379 SetupWatches(); 380 CollectEnterpriseUMAs(); 381 } 382 383 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { 384 // Reset the watches BEFORE reading the individual policies to avoid 385 // missing a change notification. 386 if (is_initialized_) 387 SetupWatches(); 388 389 // Policy scope and corresponding hive. 390 static const struct { 391 PolicyScope scope; 392 HKEY hive; 393 } kScopes[] = { 394 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, 395 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, 396 }; 397 398 bool is_enterprise = base::win::IsEnrolledToDomain(); 399 VLOG(1) << "Reading policy from the registry is " 400 << (is_enterprise ? "enabled." : "disabled."); 401 402 // Load policy data for the different scopes/levels and merge them. 403 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); 404 PolicyMap* chrome_policy = 405 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); 406 for (size_t i = 0; i < arraysize(kScopes); ++i) { 407 PolicyScope scope = kScopes[i].scope; 408 PolicyLoadStatusSample status; 409 RegistryDict gpo_dict; 410 411 // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and 412 // a matching LeaveCriticalPolicySection() call below after the 413 // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be 414 // unavailable for extended periods of time, and there are reports of this 415 // happening in the wild: http://crbug.com/265862. 416 // 417 // Blocking for minutes is neither acceptable for Chrome startup, nor on 418 // the FILE thread on which this code runs in steady state. Given that 419 // there have never been any reports of issues due to partially-applied / 420 // corrupt group policy, this code intentionally omits the 421 // EnterCriticalPolicySection() call. 422 // 423 // If there's ever reason to revisit this decision, one option could be to 424 // make the EnterCriticalPolicySection() call on a dedicated thread and 425 // timeout on it more aggressively. For now, there's no justification for 426 // the additional effort this would introduce. 427 428 if (is_enterprise || !ReadPolicyFromGPO(scope, &gpo_dict, &status)) { 429 VLOG_IF(1, !is_enterprise) << "Failed to read GPO files for " << scope 430 << " falling back to registry."; 431 gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_); 432 } 433 434 // Remove special-cased entries from the GPO dictionary. 435 scoped_ptr<RegistryDict> recommended_dict( 436 gpo_dict.RemoveKey(kKeyRecommended)); 437 scoped_ptr<RegistryDict> third_party_dict( 438 gpo_dict.RemoveKey(kKeyThirdParty)); 439 440 // Load Chrome policy. 441 LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy); 442 LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope, 443 chrome_policy); 444 445 // Load 3rd-party policy. 446 if (third_party_dict) 447 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); 448 } 449 450 return bundle.Pass(); 451 } 452 453 bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file, 454 RegistryDict* policy, 455 PolicyLoadStatusSample* status) { 456 // The following deals with the minor annoyance that Wow64 FS redirection 457 // might need to be turned off: This is the case if running as a 32-bit 458 // process on a 64-bit system, in which case Wow64 FS redirection redirects 459 // access to the %WINDIR%/System32/GroupPolicy directory to 460 // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the 461 // system-native directory. 462 if (base::PathExists(preg_file)) { 463 return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy, status); 464 } else { 465 // Try with redirection switched off. 466 ScopedDisableWow64Redirection redirection_disable; 467 if (redirection_disable.is_active() && base::PathExists(preg_file)) { 468 status->Add(POLICY_LOAD_STATUS_WOW64_REDIRECTION_DISABLED); 469 return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy, 470 status); 471 } 472 } 473 474 // Report the error. 475 LOG(ERROR) << "PReg file doesn't exist: " << preg_file.value(); 476 status->Add(POLICY_LOAD_STATUS_MISSING); 477 return false; 478 } 479 480 bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope, 481 PGROUP_POLICY_OBJECT policy_object_list, 482 RegistryDict* policy, 483 PolicyLoadStatusSample* status) { 484 RegistryDict parsed_policy; 485 RegistryDict forced_policy; 486 for (GROUP_POLICY_OBJECT* policy_object = policy_object_list; 487 policy_object; policy_object = policy_object->pNext) { 488 if (policy_object->dwOptions & GPO_FLAG_DISABLE) 489 continue; 490 491 if (PathIsUNC(policy_object->lpFileSysPath)) { 492 // UNC path: Assume this is an AD-managed machine, which updates the 493 // registry via GPO's standard registry CSE periodically. Fall back to 494 // reading from the registry in this case. 495 status->Add(POLICY_LOAD_STATUS_INACCCESSIBLE); 496 return false; 497 } 498 499 base::FilePath preg_file_path( 500 base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName)); 501 if (policy_object->dwOptions & GPO_FLAG_FORCE) { 502 RegistryDict new_forced_policy; 503 if (!ReadPRegFile(preg_file_path, &new_forced_policy, status)) 504 return false; 505 506 // Merge with existing forced policy, giving precedence to the existing 507 // forced policy. 508 new_forced_policy.Merge(forced_policy); 509 forced_policy.Swap(&new_forced_policy); 510 } else { 511 if (!ReadPRegFile(preg_file_path, &parsed_policy, status)) 512 return false; 513 } 514 } 515 516 // Merge, give precedence to forced policy. 517 parsed_policy.Merge(forced_policy); 518 policy->Swap(&parsed_policy); 519 520 return true; 521 } 522 523 bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope, 524 RegistryDict* policy, 525 PolicyLoadStatusSample* status) { 526 PGROUP_POLICY_OBJECT policy_object_list = NULL; 527 DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0; 528 if (gpo_provider_->GetAppliedGPOList( 529 flags, NULL, NULL, &kRegistrySettingsCSEGUID, 530 &policy_object_list) != ERROR_SUCCESS) { 531 PLOG(ERROR) << "GetAppliedGPOList scope " << scope; 532 status->Add(POLICY_LOAD_STATUS_QUERY_FAILED); 533 return false; 534 } 535 536 bool result = true; 537 if (policy_object_list) { 538 result = LoadGPOPolicy(scope, policy_object_list, policy, status); 539 if (!gpo_provider_->FreeGPOList(policy_object_list)) 540 LOG(WARNING) << "FreeGPOList"; 541 } else { 542 status->Add(POLICY_LOAD_STATUS_NO_POLICY); 543 } 544 545 return result; 546 } 547 548 void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict, 549 PolicyLevel level, 550 PolicyScope scope, 551 PolicyMap* chrome_policy_map) { 552 PolicyMap policy; 553 const Schema* chrome_schema = 554 schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")); 555 ParsePolicy(gpo_dict, level, scope, *chrome_schema, &policy); 556 FilterUntrustedPolicy(&policy); 557 chrome_policy_map->MergeFrom(policy); 558 } 559 560 void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict, 561 PolicyScope scope, 562 PolicyBundle* bundle) { 563 // Map of known 3rd party policy domain name to their enum values. 564 static const struct { 565 const char* name; 566 PolicyDomain domain; 567 } k3rdPartyDomains[] = { 568 { "extensions", POLICY_DOMAIN_EXTENSIONS }, 569 }; 570 571 // Policy level and corresponding path. 572 static const struct { 573 PolicyLevel level; 574 const char* path; 575 } kLevels[] = { 576 { POLICY_LEVEL_MANDATORY, kKeyMandatory }, 577 { POLICY_LEVEL_RECOMMENDED, kKeyRecommended }, 578 }; 579 580 for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) { 581 const char* name = k3rdPartyDomains[i].name; 582 const PolicyDomain domain = k3rdPartyDomains[i].domain; 583 const RegistryDict* domain_dict = gpo_dict->GetKey(name); 584 if (!domain_dict) 585 continue; 586 587 for (RegistryDict::KeyMap::const_iterator component( 588 domain_dict->keys().begin()); 589 component != domain_dict->keys().end(); 590 ++component) { 591 const PolicyNamespace policy_namespace(domain, component->first); 592 593 const Schema* schema_from_map = schema_map()->GetSchema(policy_namespace); 594 if (!schema_from_map) { 595 // This extension isn't installed or doesn't support policies. 596 continue; 597 } 598 Schema schema = *schema_from_map; 599 600 if (!schema.valid() && 601 policy_namespace.domain == POLICY_DOMAIN_EXTENSIONS && 602 policy_namespace.component_id == kLegacyBrowserSupportExtensionId) { 603 // TODO(joaodasilva): remove this special treatment for LBS by M35. 604 std::string schema_json; 605 const base::Value* value = component->second->GetValue(kKeySchema); 606 if (value && value->GetAsString(&schema_json)) { 607 std::string error; 608 schema = Schema::Parse(PatchSchema(schema_json), &error); 609 if (!schema.valid()) 610 LOG(WARNING) << "Invalid schema in the registry for LBS: " << error; 611 } 612 } 613 614 // Parse policy. 615 for (size_t j = 0; j < arraysize(kLevels); j++) { 616 const RegistryDict* policy_dict = 617 component->second->GetKey(kLevels[j].path); 618 if (!policy_dict) 619 continue; 620 621 PolicyMap policy; 622 ParsePolicy(policy_dict, kLevels[j].level, scope, schema, &policy); 623 bundle->Get(policy_namespace).MergeFrom(policy); 624 } 625 } 626 } 627 } 628 629 void PolicyLoaderWin::SetupWatches() { 630 DCHECK(is_initialized_); 631 if (!user_policy_watcher_failed_ && 632 !user_policy_watcher_.GetWatchedObject() && 633 !user_policy_watcher_.StartWatching( 634 user_policy_changed_event_.handle(), this)) { 635 DLOG(WARNING) << "Failed to start watch for user policy change event"; 636 user_policy_watcher_failed_ = true; 637 } 638 if (!machine_policy_watcher_failed_ && 639 !machine_policy_watcher_.GetWatchedObject() && 640 !machine_policy_watcher_.StartWatching( 641 machine_policy_changed_event_.handle(), this)) { 642 DLOG(WARNING) << "Failed to start watch for machine policy change event"; 643 machine_policy_watcher_failed_ = true; 644 } 645 } 646 647 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { 648 DCHECK(object == user_policy_changed_event_.handle() || 649 object == machine_policy_changed_event_.handle()) 650 << "unexpected object signaled policy reload, obj = " 651 << std::showbase << std::hex << object; 652 Reload(false); 653 } 654 655 } // namespace policy 656