Index: chrome/browser/policy/policy_loader_win.cc |
diff --git a/chrome/browser/policy/policy_loader_win.cc b/chrome/browser/policy/policy_loader_win.cc |
deleted file mode 100644 |
index 8d89206a399c381c504453f271307e2ae673ff27..0000000000000000000000000000000000000000 |
--- a/chrome/browser/policy/policy_loader_win.cc |
+++ /dev/null |
@@ -1,549 +0,0 @@ |
-// Copyright (c) 2012 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 "chrome/browser/policy/policy_loader_win.h" |
- |
-#include <windows.h> |
-#include <rpc.h> // For struct GUID |
-#include <shlwapi.h> // For PathIsUNC() |
-#include <userenv.h> // For GPO functions |
- |
-#include <string> |
-#include <vector> |
- |
-// shlwapi.dll is required for PathIsUNC(). |
-#pragma comment(lib, "shlwapi.lib") |
-// userenv.dll is required for various GPO functions. |
-#pragma comment(lib, "userenv.lib") |
- |
-#include "base/basictypes.h" |
-#include "base/file_util.h" |
-#include "base/json/json_reader.h" |
-#include "base/lazy_instance.h" |
-#include "base/logging.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 "chrome/browser/policy/policy_load_status.h" |
-#include "chrome/browser/policy/preg_parser_win.h" |
-#include "components/json_schema/json_schema_constants.h" |
-#include "components/policy/core/common/policy_bundle.h" |
-#include "components/policy/core/common/policy_map.h" |
-#include "components/policy/core/common/policy_namespace.h" |
-#include "components/policy/core/common/registry_dict_win.h" |
-#include "components/policy/core/common/schema.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 GUID of the registry settings group policy extension. |
-GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID; |
- |
-// 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: |
- virtual ~WinGPOListProvider() {} |
- |
- // AppliedGPOListProvider: |
- virtual 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); |
- } |
- |
- virtual 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; |
- |
-std::string GetSchemaTypeForValueType(base::Value::Type value_type) { |
- switch (value_type) { |
- case base::Value::TYPE_DICTIONARY: |
- return json_schema_constants::kObject; |
- case base::Value::TYPE_INTEGER: |
- return json_schema_constants::kInteger; |
- case base::Value::TYPE_LIST: |
- return json_schema_constants::kArray; |
- case base::Value::TYPE_BOOLEAN: |
- return json_schema_constants::kBoolean; |
- case base::Value::TYPE_STRING: |
- return json_schema_constants::kString; |
- default: |
- break; |
- } |
- |
- NOTREACHED() << "Unsupported policy value type " << value_type; |
- return json_schema_constants::kNull; |
-} |
- |
-// 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 base::DictionaryValue* schema, |
- PolicyMap* policy) { |
- if (!gpo_dict) |
- return; |
- |
- scoped_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); |
-} |
- |
-} // namespace |
- |
-const base::FilePath::CharType PolicyLoaderWin::kPRegFileName[] = |
- FILE_PATH_LITERAL("Registry.pol"); |
- |
-PolicyLoaderWin::PolicyLoaderWin( |
- scoped_refptr<base::SequencedTaskRunner> task_runner, |
- const 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 |
-scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create( |
- scoped_refptr<base::SequencedTaskRunner> task_runner, |
- const string16& chrome_policy_key) { |
- return make_scoped_ptr( |
- new PolicyLoaderWin(task_runner, |
- chrome_policy_key, |
- g_win_gpo_list_provider.Pointer())); |
-} |
- |
-void PolicyLoaderWin::InitOnBackgroundThread() { |
- is_initialized_ = true; |
- SetupWatches(); |
-} |
- |
-scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { |
- // Reset the watches BEFORE reading the individual policies to avoid |
- // missing a change notification. |
- if (is_initialized_) |
- SetupWatches(); |
- |
- if (chrome_policy_schema_.empty()) |
- BuildChromePolicySchema(); |
- |
- // 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 }, |
- }; |
- |
- // Load policy data for the different scopes/levels and merge them. |
- scoped_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. |
- |
- if (!ReadPolicyFromGPO(scope, &gpo_dict, &status)) { |
- VLOG(1) << "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. |
- scoped_ptr<RegistryDict> recommended_dict( |
- gpo_dict.RemoveKey(kKeyRecommended)); |
- scoped_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.Pass(); |
-} |
- |
-void PolicyLoaderWin::BuildChromePolicySchema() { |
- // TODO(joaodasilva): use the Schema directly instead of building this |
- // DictionaryValue. |
- scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue()); |
- const Schema* chrome_schema = |
- schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")); |
- for (Schema::Iterator it = chrome_schema->GetPropertiesIterator(); |
- !it.IsAtEnd(); it.Advance()) { |
- const std::string schema_type = |
- GetSchemaTypeForValueType(it.schema().type()); |
- scoped_ptr<base::DictionaryValue> entry_schema(new base::DictionaryValue()); |
- entry_schema->SetStringWithoutPathExpansion(json_schema_constants::kType, |
- schema_type); |
- |
- if (it.schema().type() == base::Value::TYPE_LIST) { |
- scoped_ptr<base::DictionaryValue> items_schema( |
- new base::DictionaryValue()); |
- items_schema->SetStringWithoutPathExpansion( |
- json_schema_constants::kType, json_schema_constants::kString); |
- entry_schema->SetWithoutPathExpansion(json_schema_constants::kItems, |
- items_schema.release()); |
- } |
- properties->SetWithoutPathExpansion(it.key(), entry_schema.release()); |
- } |
- chrome_policy_schema_.SetStringWithoutPathExpansion( |
- json_schema_constants::kType, json_schema_constants::kObject); |
- chrome_policy_schema_.SetWithoutPathExpansion( |
- json_schema_constants::kProperties, properties.release()); |
-} |
- |
-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; |
- ParsePolicy(gpo_dict, level, scope, &chrome_policy_schema_, &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) { |
- // Load the schema. |
- const base::DictionaryValue* schema_dict = NULL; |
- scoped_ptr<base::Value> schema; |
- std::string schema_json; |
- const base::Value* schema_value = component->second->GetValue(kKeySchema); |
- if (schema_value && schema_value->GetAsString(&schema_json)) { |
- schema.reset(base::JSONReader::Read(schema_json)); |
- if (!schema || !schema->GetAsDictionary(&schema_dict)) { |
- LOG(WARNING) << "Failed to parse 3rd-part policy schema for " |
- << domain << "/" << component->first; |
- } |
- } |
- |
- // 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_dict, &policy); |
- PolicyNamespace policy_namespace(domain, component->first); |
- bundle->Get(policy_namespace).MergeFrom(policy); |
- } |
- } |
- } |
-} |
- |
-void PolicyLoaderWin::SetupWatches() { |
- DCHECK(is_initialized_); |
- if (!user_policy_watcher_failed_ && |
- !user_policy_watcher_.GetWatchedObject() && |
- !user_policy_watcher_.StartWatching( |
- 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_.StartWatching( |
- 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 |