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