| // 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 <string> |
| |
| #include <string.h> |
| |
| #include <userenv.h> |
| |
| // userenv.dll is required for RegisterGPNotification(). |
| #pragma comment(lib, "userenv.lib") |
| |
| #include "base/basictypes.h" |
| #include "base/json/json_reader.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/string16.h" |
| #include "base/string_number_conversions.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "base/win/registry.h" |
| #include "chrome/browser/policy/policy_bundle.h" |
| #include "chrome/browser/policy/policy_map.h" |
| #include "chrome/common/json_schema_constants.h" |
| #include "policy/policy_constants.h" |
| |
| namespace schema = json_schema_constants; |
| |
| using base::win::RegKey; |
| using base::win::RegistryKeyIterator; |
| using base::win::RegistryValueIterator; |
| using namespace policy::registry_constants; |
| |
| namespace policy { |
| |
| namespace registry_constants { |
| const wchar_t kPathSep[] = L"\\"; |
| const wchar_t kThirdParty[] = L"3rdparty"; |
| const wchar_t kMandatory[] = L"policy"; |
| const wchar_t kRecommended[] = L"recommended"; |
| const wchar_t kSchema[] = L"schema"; |
| } // namespace registry_constants |
| |
| namespace { |
| |
| // Map of registry hives to their corresponding policy scope, in decreasing |
| // order of priority. |
| const struct { |
| HKEY hive; |
| PolicyScope scope; |
| } kHives[] = { |
| { HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE }, |
| { HKEY_CURRENT_USER, POLICY_SCOPE_USER }, |
| }; |
| |
| // Determines the registry key with the highest priority that contains |
| // the |key_path| key with a |value_name| value inside. |
| // |key_path| is a suffix to the Chrome mandatory or recommended registry key, |
| // and can be empty to lookup values directly at that key. |
| // |value_name| is the name of a value that should exist at that path. If it |
| // is empty, then only the existence of the path is verified. |
| // Returns true if |key| was updated to point to a key with a |key_path| suffix |
| // and optional |value_name| value inside. In that case, |level| and |scope| |
| // will contain the appropriate values for the key found. |
| // Returns false otherwise. |
| bool LoadHighestPriorityKey(const string16& key_path, |
| const string16& value_name, |
| RegKey* key, |
| PolicyLevel* level, |
| PolicyScope* scope) { |
| // |kKeyPaths| is in decreasing order of priority. |
| static const struct { |
| const wchar_t* path; |
| PolicyLevel level; |
| } kKeyPaths[] = { |
| { kRegistryMandatorySubKey, POLICY_LEVEL_MANDATORY }, |
| { kRegistryRecommendedSubKey, POLICY_LEVEL_RECOMMENDED }, |
| }; |
| |
| // Lookup at the mandatory path for both user and machine policies first, and |
| // then at the recommended path. |
| for (size_t k = 0; k < arraysize(kKeyPaths); ++k) { |
| for (size_t h = 0; h < arraysize(kHives); ++h) { |
| string16 path(kKeyPaths[k].path); |
| if (!key_path.empty()) |
| path += kPathSep + key_path; |
| if (key->Open(kHives[h].hive, path.c_str(), KEY_READ) != ERROR_SUCCESS || |
| !key->Valid()) { |
| continue; |
| } |
| if (value_name.empty() || key->HasValue(value_name.c_str())) { |
| *level = kKeyPaths[k].level; |
| *scope = kHives[h].scope; |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| // Reads a REG_SZ string at |key| named |name| into |result|. Returns false if |
| // the string could not be read. |
| bool ReadRegistryString(RegKey* key, |
| const string16& name, |
| string16* result) { |
| DWORD value_size = 0; |
| DWORD key_type = 0; |
| scoped_array<uint8> buffer; |
| |
| if (key->ReadValue(name.c_str(), 0, &value_size, &key_type) != ERROR_SUCCESS) |
| return false; |
| if (key_type != REG_SZ) |
| return false; |
| |
| // According to the Microsoft documentation, the string |
| // buffer may not be explicitly 0-terminated. Allocate a |
| // slightly larger buffer and pre-fill to zeros to guarantee |
| // the 0-termination. |
| buffer.reset(new uint8[value_size + 2]); |
| memset(buffer.get(), 0, value_size + 2); |
| key->ReadValue(name.c_str(), buffer.get(), &value_size, NULL); |
| result->assign(reinterpret_cast<const wchar_t*>(buffer.get())); |
| return true; |
| } |
| |
| // Reads a REG_DWORD integer at |key| named |name| into |result|. Returns false |
| // if the value could no be read. |
| bool ReadRegistryInteger(RegKey* key, |
| const string16& name, |
| uint32* result) { |
| DWORD dword; |
| if (key->ReadValueDW(name.c_str(), &dword) != ERROR_SUCCESS) |
| return false; |
| *result = dword; |
| return true; |
| } |
| |
| // Returns the Value for a Chrome string policy named |name|, or NULL if |
| // it wasn't found. The caller owns the returned value. |
| base::Value* ReadChromeStringValue(const string16& name, |
| PolicyLevel* level, |
| PolicyScope* scope) { |
| RegKey key; |
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) |
| return NULL; |
| string16 value; |
| if (!ReadRegistryString(&key, name, &value)) |
| return NULL; |
| return base::Value::CreateStringValue(value); |
| } |
| |
| // Returns the Value for a Chrome string list policy named |name|, |
| // or NULL if it wasn't found. The caller owns the returned value. |
| base::Value* ReadChromeStringListValue(const string16& name, |
| PolicyLevel* level, |
| PolicyScope* scope) { |
| RegKey key; |
| if (!LoadHighestPriorityKey(name, string16(), &key, level, scope)) |
| return NULL; |
| base::ListValue* result = new base::ListValue(); |
| string16 value; |
| int index = 0; |
| while (ReadRegistryString(&key, base::IntToString16(++index), &value)) |
| result->Append(base::Value::CreateStringValue(value)); |
| return result; |
| } |
| |
| // Returns the Value for a Chrome boolean policy named |name|, |
| // or NULL if it wasn't found. The caller owns the returned value. |
| base::Value* ReadChromeBooleanValue(const string16& name, |
| PolicyLevel* level, |
| PolicyScope* scope) { |
| RegKey key; |
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) |
| return NULL; |
| uint32 value; |
| if (!ReadRegistryInteger(&key, name, &value)) |
| return NULL; |
| return base::Value::CreateBooleanValue(value != 0u); |
| } |
| |
| // Returns the Value for a Chrome integer policy named |name|, |
| // or NULL if it wasn't found. The caller owns the returned value. |
| base::Value* ReadChromeIntegerValue(const string16& name, |
| PolicyLevel* level, |
| PolicyScope* scope) { |
| RegKey key; |
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) |
| return NULL; |
| uint32 value; |
| if (!ReadRegistryInteger(&key, name, &value)) |
| return NULL; |
| return base::Value::CreateIntegerValue(value); |
| } |
| |
| // Returns the Value for a Chrome dictionary policy named |name|, |
| // or NULL if it wasn't found. The caller owns the returned value. |
| base::Value* ReadChromeDictionaryValue(const string16& name, |
| PolicyLevel* level, |
| PolicyScope* scope) { |
| // Dictionaries are encoded as JSON strings on Windows. |
| // |
| // A dictionary could be stored as a subkey, with each of its entries |
| // within that subkey. However, it would be impossible to recover the |
| // type for some of those entries: |
| // - Booleans are stored as DWORDS and are indistinguishable from |
| // integers; |
| // - Lists are stored as a subkey, with entries mapping 0 to N-1 to |
| // their value. This is indistinguishable from a Dictionary with |
| // integer keys. |
| // |
| // The GPO policy editor also has a limited data entry form that doesn't |
| // support dictionaries. |
| RegKey key; |
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) |
| return NULL; |
| string16 value; |
| if (!ReadRegistryString(&key, name, &value)) |
| return NULL; |
| return base::JSONReader::Read(UTF16ToUTF8(value)); |
| } |
| |
| // Returns the Value type described in |schema|, or |default_type| if not found. |
| base::Value::Type GetType(const base::DictionaryValue* schema, |
| base::Value::Type default_type) { |
| // JSON-schema types to base::Value::Type mapping. |
| static const struct { |
| // JSON schema type. |
| const char* schema_type; |
| // Correspondent value type. |
| base::Value::Type value_type; |
| } kSchemaToValueTypeMap[] = { |
| { schema::kArray, base::Value::TYPE_LIST }, |
| { schema::kBoolean, base::Value::TYPE_BOOLEAN }, |
| { schema::kInteger, base::Value::TYPE_INTEGER }, |
| { schema::kNull, base::Value::TYPE_NULL }, |
| { schema::kNumber, base::Value::TYPE_DOUBLE }, |
| { schema::kObject, base::Value::TYPE_DICTIONARY }, |
| { schema::kString, base::Value::TYPE_STRING }, |
| }; |
| |
| if (!schema) |
| return default_type; |
| std::string type; |
| if (!schema->GetString(schema::kType, &type)) |
| return default_type; |
| for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) { |
| if (type == kSchemaToValueTypeMap[i].schema_type) |
| return kSchemaToValueTypeMap[i].value_type; |
| } |
| return default_type; |
| } |
| |
| // Returns the default type for registry entries of |reg_type|, when there is |
| // no schema defined type for a policy. |
| base::Value::Type GetDefaultFor(DWORD reg_type) { |
| return reg_type == REG_DWORD ? base::Value::TYPE_INTEGER : |
| base::Value::TYPE_STRING; |
| } |
| |
| // Returns the entry with key |name| in |dictionary| (can be NULL), or NULL. |
| const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary, |
| const std::string& name) { |
| if (!dictionary) |
| return NULL; |
| const base::DictionaryValue* entry = NULL; |
| dictionary->GetDictionary(name, &entry); |
| return entry; |
| } |
| |
| // Returns the schema for property |name| given the |schema| of an object. |
| // Returns the "additionalProperties" schema if no specific schema for |
| // |name| is present. Returns NULL if no schema is found. |
| const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema, |
| const std::string& name) { |
| const base::DictionaryValue* properties = |
| GetEntry(schema, schema::kProperties); |
| const base::DictionaryValue* sub_schema = GetEntry(properties, name); |
| if (sub_schema) |
| return sub_schema; |
| // "additionalProperties" can be a boolean, but that case is ignored. |
| return GetEntry(schema, schema::kAdditionalProperties); |
| } |
| |
| // Converts string |value| to another |type|, if possible. |
| base::Value* ConvertComponentStringValue(const string16& value, |
| base::Value::Type type) { |
| switch (type) { |
| case base::Value::TYPE_NULL: |
| return base::Value::CreateNullValue(); |
| |
| case base::Value::TYPE_BOOLEAN: { |
| int int_value; |
| if (base::StringToInt(value, &int_value)) |
| return base::Value::CreateBooleanValue(int_value != 0); |
| return NULL; |
| } |
| |
| case base::Value::TYPE_INTEGER: { |
| int int_value; |
| if (base::StringToInt(value, &int_value)) |
| return base::Value::CreateIntegerValue(int_value); |
| return NULL; |
| } |
| |
| case base::Value::TYPE_DOUBLE: { |
| double double_value; |
| if (base::StringToDouble(UTF16ToUTF8(value), &double_value)) |
| return base::Value::CreateDoubleValue(double_value); |
| DLOG(WARNING) << "Failed to read policy value as double: " << value; |
| return NULL; |
| } |
| |
| case base::Value::TYPE_STRING: |
| return base::Value::CreateStringValue(value); |
| |
| case base::Value::TYPE_DICTIONARY: |
| case base::Value::TYPE_LIST: |
| return base::JSONReader::Read(UTF16ToUTF8(value)); |
| |
| case base::Value::TYPE_BINARY: |
| DLOG(WARNING) << "Cannot convert REG_SZ entry to type " << type; |
| return NULL; |
| } |
| NOTREACHED(); |
| return NULL; |
| } |
| |
| // Converts an integer |value| to another |type|, if possible. |
| base::Value* ConvertComponentIntegerValue(uint32 value, |
| base::Value::Type type) { |
| switch (type) { |
| case base::Value::TYPE_BOOLEAN: |
| return base::Value::CreateBooleanValue(value != 0); |
| |
| case base::Value::TYPE_INTEGER: |
| return base::Value::CreateIntegerValue(value); |
| |
| case base::Value::TYPE_DOUBLE: |
| return base::Value::CreateDoubleValue(value); |
| |
| case base::Value::TYPE_NULL: |
| case base::Value::TYPE_STRING: |
| case base::Value::TYPE_BINARY: |
| case base::Value::TYPE_DICTIONARY: |
| case base::Value::TYPE_LIST: |
| DLOG(WARNING) << "Cannot convert REG_DWORD entry to type " << type; |
| return NULL; |
| } |
| NOTREACHED(); |
| return NULL; |
| } |
| |
| // Reads a simple (non-Dictionary, non-Array) value from the registry |key| |
| // named |name| with registry type |reg_type| as a value of type |type|. |
| // Returns NULL if the value could not be loaded or converted. |
| base::Value* ReadComponentSimpleValue(RegKey* key, |
| const string16& name, |
| DWORD reg_type, |
| base::Value::Type type) { |
| switch (reg_type) { |
| case REG_SZ: { |
| string16 value; |
| if (ReadRegistryString(key, name, &value)) |
| return ConvertComponentStringValue(value, type); |
| break; |
| } |
| |
| case REG_DWORD: { |
| uint32 value; |
| if (ReadRegistryInteger(key, name, &value)) |
| return ConvertComponentIntegerValue(value, type); |
| break; |
| } |
| |
| default: |
| DLOG(WARNING) << "Registry type not supported for key " << name; |
| break; |
| } |
| NOTREACHED(); |
| return NULL; |
| } |
| |
| // Forward declaration for ReadComponentListValue(). |
| base::DictionaryValue* ReadComponentDictionaryValue( |
| HKEY hive, |
| const string16& path, |
| const base::DictionaryValue* schema); |
| |
| // Loads the list at |path| in the given |hive|. |schema| is a JSON schema |
| // (http://json-schema.org/) that describes the expected type of the list. |
| // Ownership of the result is transferred to the caller. |
| base::ListValue* ReadComponentListValue(HKEY hive, |
| const string16& path, |
| const base::DictionaryValue* schema) { |
| // The sub-elements are indexed from 1 to N. They can be represented as |
| // registry values or registry keys though; use |schema| first to try to |
| // determine the right type, and if that fails default to STRING. |
| |
| RegKey key(hive, path.c_str(), KEY_READ); |
| if (!key.Valid()) |
| return NULL; |
| |
| // Get the schema for list items. |
| schema = GetEntry(schema, schema::kItems); |
| base::Value::Type type = GetType(schema, base::Value::TYPE_STRING); |
| base::ListValue* list = new base::ListValue(); |
| for (int i = 1; ; ++i) { |
| string16 name = base::IntToString16(i); |
| base::Value* value = NULL; |
| if (type == base::Value::TYPE_DICTIONARY) { |
| value = |
| ReadComponentDictionaryValue(hive, path + kPathSep + name, schema); |
| } else if (type == base::Value::TYPE_LIST) { |
| value = ReadComponentListValue(hive, path + kPathSep + name, schema); |
| } else { |
| DWORD reg_type; |
| key.ReadValue(name.c_str(), NULL, NULL, ®_type); |
| if (reg_type != REG_NONE) |
| value = ReadComponentSimpleValue(&key, name, reg_type, type); |
| } |
| if (value) |
| list->Append(value); |
| else |
| break; |
| } |
| return list; |
| } |
| |
| // Loads the dictionary at |path| in the given |hive|. |schema| is a JSON |
| // schema (http://json-schema.org/) that describes the expected types for the |
| // dictionary entries. When the type for a certain entry isn't described in the |
| // schema, a default conversion takes place. |schema| can be NULL. |
| // Ownership of the result is transferred to the caller. |
| base::DictionaryValue* ReadComponentDictionaryValue( |
| HKEY hive, |
| const string16& path, |
| const base::DictionaryValue* schema) { |
| // A "value" in the registry is like a file in a filesystem, and a "key" is |
| // like a directory, that contains other "values" and "keys". |
| // Unfortunately it is possible to have a name both as a "value" and a "key". |
| // In those cases, the sub "key" will be ignored; this choice is arbitrary. |
| |
| // First iterate over all the "values" in |path| and convert them; then |
| // recurse into each "key" in |path| and convert them as dictionaries. |
| |
| RegKey key(hive, path.c_str(), KEY_READ); |
| if (!key.Valid()) |
| return NULL; |
| |
| base::DictionaryValue* dict = new base::DictionaryValue(); |
| for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) { |
| string16 name16(it.Name()); |
| std::string name(UTF16ToUTF8(name16)); |
| const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name); |
| base::Value::Type type = GetType(sub_schema, GetDefaultFor(it.Type())); |
| base::Value* value = |
| ReadComponentSimpleValue(&key, name16, it.Type(), type); |
| if (value) |
| dict->Set(name, value); |
| } |
| |
| for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) { |
| string16 name16(it.Name()); |
| std::string name(UTF16ToUTF8(name16)); |
| if (dict->HasKey(name)) { |
| DLOG(WARNING) << "Ignoring registry key because a value exists with the " |
| "same name: " << path << kPathSep << name; |
| continue; |
| } |
| |
| const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name); |
| base::Value::Type type = GetType(sub_schema, base::Value::TYPE_DICTIONARY); |
| base::Value* value = NULL; |
| const string16 sub_path = path + kPathSep + name16; |
| if (type == base::Value::TYPE_DICTIONARY) { |
| value = ReadComponentDictionaryValue(hive, sub_path, sub_schema); |
| } else if (type == base::Value::TYPE_LIST) { |
| value = ReadComponentListValue(hive, sub_path, sub_schema); |
| } else { |
| DLOG(WARNING) << "Can't read a simple type in registry key at " << path; |
| } |
| if (value) |
| dict->Set(name, value); |
| } |
| |
| return dict; |
| } |
| |
| // Reads a JSON schema from the given |registry_value|, at the given |
| // |registry_key| in |hive|. |registry_value| must be a string (REG_SZ), and |
| // is decoded as JSON data. Returns NULL on failure. Ownership is transferred |
| // to the caller. |
| base::DictionaryValue* ReadRegistrySchema(HKEY hive, |
| const string16& registry_key, |
| const string16& registry_value) { |
| RegKey key(hive, registry_key.c_str(), KEY_READ); |
| string16 schema; |
| if (!ReadRegistryString(&key, registry_value, &schema)) |
| return NULL; |
| // A JSON schema is represented in JSON too. |
| scoped_ptr<base::Value> value(base::JSONReader::Read(UTF16ToUTF8(schema))); |
| if (!value.get()) |
| return NULL; |
| base::DictionaryValue* dict = NULL; |
| if (!value->GetAsDictionary(&dict)) |
| return NULL; |
| // The top-level entry must be an object, and each of its properties maps |
| // a policy name to its schema. |
| if (GetType(dict, base::Value::TYPE_DICTIONARY) != |
| base::Value::TYPE_DICTIONARY) { |
| DLOG(WARNING) << "schema top-level type isn't \"object\""; |
| return NULL; |
| } |
| value.release(); |
| return dict; |
| } |
| |
| } // namespace |
| |
| PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list) |
| : is_initialized_(false), |
| policy_list_(policy_list), |
| 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() { |
| user_policy_watcher_.StopWatching(); |
| machine_policy_watcher_.StopWatching(); |
| } |
| |
| void PolicyLoaderWin::InitOnFile() { |
| is_initialized_ = true; |
| SetupWatches(); |
| } |
| |
| scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { |
| scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); |
| LoadChromePolicy(&bundle->Get(POLICY_DOMAIN_CHROME, std::string())); |
| Load3rdPartyPolicies(bundle.get()); |
| return bundle.Pass(); |
| } |
| |
| void PolicyLoaderWin::LoadChromePolicy(PolicyMap* chrome_policies) { |
| // Reset the watches BEFORE reading the individual policies to avoid |
| // missing a change notification. |
| if (is_initialized_) |
| SetupWatches(); |
| |
| const PolicyDefinitionList::Entry* current; |
| for (current = policy_list_->begin; current != policy_list_->end; ++current) { |
| const string16 name(ASCIIToUTF16(current->name)); |
| PolicyLevel level = POLICY_LEVEL_MANDATORY; |
| PolicyScope scope = POLICY_SCOPE_MACHINE; |
| base::Value* value = NULL; |
| |
| switch (current->value_type) { |
| case base::Value::TYPE_STRING: |
| value = ReadChromeStringValue(name, &level, &scope); |
| break; |
| |
| case base::Value::TYPE_LIST: |
| value = ReadChromeStringListValue(name, &level, &scope); |
| break; |
| |
| case base::Value::TYPE_BOOLEAN: |
| value = ReadChromeBooleanValue(name, &level, &scope); |
| break; |
| |
| case base::Value::TYPE_INTEGER: |
| value = ReadChromeIntegerValue(name, &level, &scope); |
| break; |
| |
| case base::Value::TYPE_DICTIONARY: |
| value = ReadChromeDictionaryValue(name, &level, &scope); |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| |
| if (value) |
| chrome_policies->Set(current->name, level, scope, value); |
| } |
| } |
| |
| void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) { |
| // Each 3rd party namespace can have policies on both HKLM and HKCU. They |
| // should be merged, giving priority to HKLM for policies with the same name. |
| |
| // Map of known domain name to their enum values. |
| static const struct { |
| const char* name; |
| PolicyDomain domain; |
| } kDomains[] = { |
| { "extensions", POLICY_DOMAIN_EXTENSIONS }, |
| }; |
| |
| // Map of policy paths to their corresponding policy level, in decreasing |
| // order of priority. |
| static const struct { |
| const char* path; |
| PolicyLevel level; |
| } kKeyPaths[] = { |
| { "policy", POLICY_LEVEL_MANDATORY }, |
| { "recommended", POLICY_LEVEL_RECOMMENDED }, |
| }; |
| |
| // Path where policies for components are stored. |
| const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep + |
| kThirdParty + kPathSep; |
| |
| for (size_t h = 0; h < arraysize(kHives); ++h) { |
| HKEY hkey = kHives[h].hive; |
| |
| for (size_t d = 0; d < arraysize(kDomains); ++d) { |
| // Each subkey under this domain is a component of that domain. |
| // |domain_path| == SOFTWARE\Policies\Chromium\3rdparty\<domain> |
| string16 domain_path = kPathPrefix + ASCIIToUTF16(kDomains[d].name); |
| |
| for (RegistryKeyIterator domain_iterator(hkey, domain_path.c_str()); |
| domain_iterator.Valid(); ++domain_iterator) { |
| string16 component(domain_iterator.Name()); |
| string16 component_path = domain_path + kPathSep + component; |
| |
| // Load the schema for this component's policy, if present. |
| scoped_ptr<base::DictionaryValue> schema( |
| ReadRegistrySchema(hkey, component_path, kSchema)); |
| |
| for (size_t k = 0; k < arraysize(kKeyPaths); ++k) { |
| string16 path = |
| component_path + kPathSep + ASCIIToUTF16(kKeyPaths[k].path); |
| |
| scoped_ptr<base::DictionaryValue> dictionary( |
| ReadComponentDictionaryValue(hkey, path, schema.get())); |
| if (dictionary.get()) { |
| PolicyMap policies; |
| policies.LoadFrom( |
| dictionary.get(), kKeyPaths[k].level, kHives[h].scope); |
| // LoadFrom() overwrites any existing values. Use a temporary map |
| // and then use MergeFrom(), that only overwrites values with lower |
| // priority. |
| bundle->Get(kDomains[d].domain, UTF16ToUTF8(component)) |
| .MergeFrom(policies); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| 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 |