| 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
|
| index df5754eda04dc3be261e6b4daacb9f2a13303d80..36a44fea38450e53e1a55832ad4f4d7e2a503c2f 100644
|
| --- a/chrome/browser/policy/policy_loader_win.cc
|
| +++ b/chrome/browser/policy/policy_loader_win.cc
|
| @@ -29,16 +29,23 @@
|
| using base::win::RegKey;
|
| using base::win::RegistryKeyIterator;
|
| using base::win::RegistryValueIterator;
|
| +using namespace policy::registry_constants;
|
|
|
| namespace policy {
|
|
|
| -namespace {
|
| -
|
| -// Suffix of kRegistryMandatorySubKey where 3rd party policies are stored.
|
| -const char k3rdPartyPolicySubKey[] = "\\3rdparty\\";
|
| +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";
|
| + const char kType[] = "type";
|
| + const char kProperties[] = "properties";
|
| + const char kAdditionalProperties[] = "additionalProperties";
|
| + const char kItems[] = "items";
|
| +} // namespace registry_constants
|
|
|
| -// Path separator for registry keys.
|
| -const wchar_t kPathSep[] = L"\\";
|
| +namespace {
|
|
|
| // Map of registry hives to their corresponding policy scope, in decreasing
|
| // order of priority.
|
| @@ -80,7 +87,7 @@ bool LoadHighestPriorityKey(const string16& key_path,
|
| for (size_t h = 0; h < arraysize(kHives); ++h) {
|
| string16 path(kKeyPaths[k].path);
|
| if (!key_path.empty())
|
| - path += ASCIIToUTF16("\\") + key_path;
|
| + path += kPathSep + key_path;
|
| key->Open(kHives[h].hive, path.c_str(), KEY_READ);
|
| if (!key->Valid())
|
| continue;
|
| @@ -133,9 +140,9 @@ bool ReadRegistryInteger(RegKey* key,
|
|
|
| // 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* ReadStringValue(const string16& name,
|
| - PolicyLevel* level,
|
| - PolicyScope* scope) {
|
| +base::Value* ReadChromeStringValue(const string16& name,
|
| + PolicyLevel* level,
|
| + PolicyScope* scope) {
|
| RegKey key;
|
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
|
| return NULL;
|
| @@ -147,9 +154,9 @@ base::Value* ReadStringValue(const string16& name,
|
|
|
| // 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* ReadStringListValue(const string16& name,
|
| - PolicyLevel* level,
|
| - PolicyScope* scope) {
|
| +base::Value* ReadChromeStringListValue(const string16& name,
|
| + PolicyLevel* level,
|
| + PolicyScope* scope) {
|
| RegKey key;
|
| if (!LoadHighestPriorityKey(name, string16(), &key, level, scope))
|
| return NULL;
|
| @@ -163,9 +170,9 @@ base::Value* ReadStringListValue(const string16& name,
|
|
|
| // 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* ReadBooleanValue(const string16& name,
|
| - PolicyLevel* level,
|
| - PolicyScope* scope) {
|
| +base::Value* ReadChromeBooleanValue(const string16& name,
|
| + PolicyLevel* level,
|
| + PolicyScope* scope) {
|
| RegKey key;
|
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
|
| return NULL;
|
| @@ -177,9 +184,9 @@ base::Value* ReadBooleanValue(const string16& name,
|
|
|
| // 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* ReadIntegerValue(const string16& name,
|
| - PolicyLevel* level,
|
| - PolicyScope* scope) {
|
| +base::Value* ReadChromeIntegerValue(const string16& name,
|
| + PolicyLevel* level,
|
| + PolicyScope* scope) {
|
| RegKey key;
|
| if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
|
| return NULL;
|
| @@ -191,9 +198,9 @@ base::Value* ReadIntegerValue(const string16& name,
|
|
|
| // 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* ReadDictionaryValue(const string16& name,
|
| - PolicyLevel* level,
|
| - PolicyScope* scope) {
|
| +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
|
| @@ -216,10 +223,219 @@ base::Value* ReadDictionaryValue(const string16& name,
|
| return base::JSONReader::Read(UTF16ToUTF8(value));
|
| }
|
|
|
| -// Loads the dictionary at |path| in the given |hive|. Ownership is transferred
|
| -// to the caller.
|
| -base::DictionaryValue* ReadRegistryDictionaryValue(HKEY hive,
|
| - const string16& path) {
|
| +// 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[] = {
|
| + { "array", base::Value::TYPE_LIST },
|
| + { "boolean", base::Value::TYPE_BOOLEAN },
|
| + { "integer", base::Value::TYPE_INTEGER },
|
| + { "null", base::Value::TYPE_NULL },
|
| + { "number", base::Value::TYPE_DOUBLE },
|
| + { "object", base::Value::TYPE_DICTIONARY },
|
| + { "string", base::Value::TYPE_STRING },
|
| + };
|
| +
|
| + if (!schema)
|
| + return default_type;
|
| + std::string type;
|
| + if (!schema->GetString(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.
|
| +base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
|
| + const std::string& name) {
|
| + if (!dictionary)
|
| + return NULL;
|
| + 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.
|
| +base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
|
| + const std::string& name) {
|
| + base::DictionaryValue* properties = GetEntry(schema, kProperties);
|
| + 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, 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, 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);
|
| + value = ReadComponentSimpleValue(&key, name, reg_type, type);
|
| + }
|
| + if (!value)
|
| + break;
|
| + list->Append(value);
|
| + }
|
| + 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".
|
| @@ -228,43 +444,42 @@ base::DictionaryValue* ReadRegistryDictionaryValue(HKEY hive,
|
| // First iterate over all the "values" in |path| and convert them; then
|
| // recurse into each "key" in |path| and convert them as dictionaries.
|
|
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| 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 name(it.Name());
|
| - switch (it.Type()) {
|
| - case REG_SZ: {
|
| - string16 value;
|
| - if (ReadRegistryString(&key, name, &value))
|
| - dict->SetString(UTF16ToUTF8(name), value);
|
| - break;
|
| - }
|
| -
|
| - case REG_DWORD: {
|
| - uint32 value;
|
| - if (ReadRegistryInteger(&key, name, &value))
|
| - dict->SetInteger(UTF16ToUTF8(name), value);
|
| - break;
|
| - }
|
| -
|
| - default:
|
| - // TODO(joaodasilva): use a schema to determine the correct types.
|
| - LOG(WARNING) << "Ignoring registry value with unsupported type: "
|
| - << path << kPathSep << name;
|
| - }
|
| + 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)) {
|
| - LOG(WARNING) << "Ignoring registry key because a value exists with the "
|
| - "same name: " << path << kPathSep << name;
|
| + DLOG(WARNING) << "Ignoring registry key because a value exists with the "
|
| + "same name: " << path << kPathSep << name;
|
| continue;
|
| }
|
| - base::DictionaryValue* value =
|
| - ReadRegistryDictionaryValue(hive, path + kPathSep + name16);
|
| +
|
| + 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);
|
| }
|
| @@ -272,6 +487,35 @@ base::DictionaryValue* ReadRegistryDictionaryValue(HKEY hive,
|
| 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)
|
| @@ -323,23 +567,23 @@ void PolicyLoaderWin::LoadChromePolicy(PolicyMap* chrome_policies) {
|
|
|
| switch (current->value_type) {
|
| case base::Value::TYPE_STRING:
|
| - value = ReadStringValue(name, &level, &scope);
|
| + value = ReadChromeStringValue(name, &level, &scope);
|
| break;
|
|
|
| case base::Value::TYPE_LIST:
|
| - value = ReadStringListValue(name, &level, &scope);
|
| + value = ReadChromeStringListValue(name, &level, &scope);
|
| break;
|
|
|
| case base::Value::TYPE_BOOLEAN:
|
| - value = ReadBooleanValue(name, &level, &scope);
|
| + value = ReadChromeBooleanValue(name, &level, &scope);
|
| break;
|
|
|
| case base::Value::TYPE_INTEGER:
|
| - value = ReadIntegerValue(name, &level, &scope);
|
| + value = ReadChromeIntegerValue(name, &level, &scope);
|
| break;
|
|
|
| case base::Value::TYPE_DICTIONARY:
|
| - value = ReadDictionaryValue(name, &level, &scope);
|
| + value = ReadChromeDictionaryValue(name, &level, &scope);
|
| break;
|
|
|
| default:
|
| @@ -374,8 +618,8 @@ void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) {
|
| };
|
|
|
| // Path where policies for components are stored.
|
| - const string16 kPathPrefix =
|
| - kRegistryMandatorySubKey + ASCIIToUTF16(k3rdPartyPolicySubKey);
|
| + const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep +
|
| + kThirdParty + kPathSep;
|
|
|
| for (size_t h = 0; h < arraysize(kHives); ++h) {
|
| HKEY hkey = kHives[h].hive;
|
| @@ -390,12 +634,16 @@ void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) {
|
| 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(
|
| - ReadRegistryDictionaryValue(hkey, path));
|
| + ReadComponentDictionaryValue(hkey, path, schema.get()));
|
| if (dictionary.get()) {
|
| PolicyMap policies;
|
| policies.LoadFrom(
|
|
|