Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/policy/policy_loader_win.h" | 5 #include "chrome/browser/policy/policy_loader_win.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include <string.h> | 9 #include <string.h> |
| 10 | 10 |
| 11 #include <rpc.h> // For struct GUID | |
| 12 #include <shlwapi.h> // For PathIsUNC() | |
| 11 #include <userenv.h> | 13 #include <userenv.h> |
| 12 | 14 |
| 13 // userenv.dll is required for RegisterGPNotification(). | 15 // userenv.dll is required for RegisterGPNotification() and GetAppliedGPOList(). |
| 14 #pragma comment(lib, "userenv.lib") | 16 #pragma comment(lib, "userenv.lib") |
| 17 // shlwapi.dll is required for PathIsUNC(). | |
| 18 #pragma comment(lib, "shlwapi.lib") | |
|
Joao da Silva
2013/04/05 13:55:01
nit: order
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 15 | 19 |
| 16 #include "base/basictypes.h" | 20 #include "base/basictypes.h" |
| 21 #include "base/files/file_path.h" | |
| 17 #include "base/json/json_reader.h" | 22 #include "base/json/json_reader.h" |
| 18 #include "base/logging.h" | 23 #include "base/logging.h" |
| 19 #include "base/memory/scoped_ptr.h" | |
| 20 #include "base/string16.h" | 24 #include "base/string16.h" |
| 25 #include "base/string_util.h" | |
| 21 #include "base/strings/string_number_conversions.h" | 26 #include "base/strings/string_number_conversions.h" |
| 27 #include "base/sys_byteorder.h" | |
| 22 #include "base/utf_string_conversions.h" | 28 #include "base/utf_string_conversions.h" |
| 23 #include "base/values.h" | |
| 24 #include "base/win/registry.h" | 29 #include "base/win/registry.h" |
| 25 #include "chrome/browser/policy/policy_bundle.h" | 30 #include "chrome/browser/policy/policy_bundle.h" |
| 26 #include "chrome/browser/policy/policy_map.h" | 31 #include "chrome/browser/policy/policy_map.h" |
| 32 #include "chrome/browser/policy/preg_parser_win.h" | |
| 27 #include "chrome/common/json_schema/json_schema_constants.h" | 33 #include "chrome/common/json_schema/json_schema_constants.h" |
| 28 #include "policy/policy_constants.h" | 34 #include "policy/policy_constants.h" |
| 29 | 35 |
| 30 namespace schema = json_schema_constants; | 36 namespace schema = json_schema_constants; |
| 31 | 37 |
| 32 using base::win::RegKey; | 38 using base::win::RegKey; |
| 33 using base::win::RegistryKeyIterator; | 39 using base::win::RegistryKeyIterator; |
| 34 using base::win::RegistryValueIterator; | 40 using base::win::RegistryValueIterator; |
| 35 using namespace policy::registry_constants; | 41 using namespace policy::registry_constants; |
| 36 | 42 |
| 37 namespace policy { | 43 namespace policy { |
| 38 | 44 |
| 39 namespace registry_constants { | 45 namespace registry_constants { |
| 40 const wchar_t kPathSep[] = L"\\"; | 46 const wchar_t kPathSep[] = L"\\"; |
| 41 const wchar_t kThirdParty[] = L"3rdparty"; | 47 const wchar_t kThirdParty[] = L"3rdparty"; |
| 42 const wchar_t kMandatory[] = L"policy"; | 48 const wchar_t kMandatory[] = L"policy"; |
| 43 const wchar_t kRecommended[] = L"recommended"; | 49 const wchar_t kRecommended[] = L"recommended"; |
| 44 const wchar_t kSchema[] = L"schema"; | 50 const wchar_t kSchema[] = L"schema"; |
| 45 } // namespace registry_constants | 51 } // namespace registry_constants |
| 46 | 52 |
| 47 namespace { | 53 namespace { |
| 48 | 54 |
| 49 // Map of registry hives to their corresponding policy scope, in decreasing | 55 const char kKeyMandatory[] = "policy"; |
| 50 // order of priority. | 56 const char kKeyRecommended[] = "recommended"; |
| 51 const struct { | 57 const char kKeySchema[] = "schema"; |
| 52 HKEY hive; | 58 const char kKeyThirdParty[] = "3rdparty"; |
| 53 PolicyScope scope; | 59 |
| 54 } kHives[] = { | 60 // The GUID of the registry settings group policy extension. |
| 55 { HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE }, | 61 GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID; |
| 56 { HKEY_CURRENT_USER, POLICY_SCOPE_USER }, | 62 |
| 63 // The PReg file name. | |
| 64 const base::FilePath::CharType kPRegFileName[] = | |
| 65 FILE_PATH_LITERAL("Registry.pol"); | |
| 66 | |
| 67 // Deleter for using GPO lists with scoped_ptr. | |
| 68 struct FreeGPOListDeleter { | |
| 69 inline void operator()(GROUP_POLICY_OBJECT* ptr) const { | |
| 70 FreeGPOList(ptr); | |
| 71 } | |
| 57 }; | 72 }; |
| 58 | 73 |
| 59 // Reads a REG_SZ string at |key| named |name| into |result|. Returns false if | 74 // Returns the entry with key |name| in |dictionary| (can be NULL), or NULL. |
| 60 // the string could not be read. | 75 const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary, |
| 61 bool ReadRegistryString(RegKey* key, | 76 const std::string& name) { |
| 62 const string16& name, | 77 if (!dictionary) |
| 63 string16* result) { | 78 return NULL; |
| 64 DWORD value_size = 0; | 79 const base::DictionaryValue* entry = NULL; |
| 65 DWORD key_type = 0; | 80 dictionary->GetDictionaryWithoutPathExpansion(name, &entry); |
| 66 scoped_array<uint8> buffer; | 81 return entry; |
| 67 | |
| 68 if (key->ReadValue(name.c_str(), 0, &value_size, &key_type) != ERROR_SUCCESS) | |
| 69 return false; | |
| 70 if (key_type != REG_SZ) | |
| 71 return false; | |
| 72 | |
| 73 // According to the Microsoft documentation, the string | |
| 74 // buffer may not be explicitly 0-terminated. Allocate a | |
| 75 // slightly larger buffer and pre-fill to zeros to guarantee | |
| 76 // the 0-termination. | |
| 77 buffer.reset(new uint8[value_size + 2]); | |
| 78 memset(buffer.get(), 0, value_size + 2); | |
| 79 key->ReadValue(name.c_str(), buffer.get(), &value_size, NULL); | |
| 80 result->assign(reinterpret_cast<const wchar_t*>(buffer.get())); | |
| 81 return true; | |
| 82 } | 82 } |
| 83 | 83 |
| 84 // Reads a REG_DWORD integer at |key| named |name| into |result|. Returns false | 84 // Tries to extract the dictionary at |key| in |dict| and returns it. |
| 85 // if the value could no be read. | 85 scoped_ptr<base::DictionaryValue> RemoveDict(base::DictionaryValue* dict, |
| 86 bool ReadRegistryInteger(RegKey* key, | 86 const std::string& key) { |
| 87 const string16& name, | 87 base::Value* entry = NULL; |
| 88 uint32* result) { | 88 base::DictionaryValue* result_dict = NULL; |
| 89 DWORD dword; | 89 if (dict && dict->RemoveWithoutPathExpansion(key, &entry) && entry) |
| 90 if (key->ReadValueDW(name.c_str(), &dword) != ERROR_SUCCESS) | 90 entry->GetAsDictionary(&result_dict); |
|
Joao da Silva
2013/04/05 13:55:01
This leaks |entry| if it's not a dictionary.
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 91 return false; | 91 |
| 92 *result = dword; | 92 return make_scoped_ptr(result_dict); |
| 93 return true; | 93 } |
| 94 | |
| 95 std::string GetSchemaTypeForValueType(base::Value::Type value_type) { | |
| 96 switch (value_type) { | |
| 97 case base::Value::TYPE_DICTIONARY: | |
| 98 return json_schema_constants::kObject; | |
| 99 case base::Value::TYPE_INTEGER: | |
| 100 return json_schema_constants::kInteger; | |
| 101 case base::Value::TYPE_LIST: | |
| 102 return json_schema_constants::kArray; | |
| 103 case base::Value::TYPE_BOOLEAN: | |
| 104 return json_schema_constants::kBoolean; | |
| 105 case base::Value::TYPE_STRING: | |
| 106 return json_schema_constants::kString; | |
| 107 default: | |
| 108 break; | |
| 109 } | |
| 110 | |
| 111 NOTREACHED() << "Unsupported policy value type " << value_type; | |
| 112 return json_schema_constants::kNull; | |
| 94 } | 113 } |
| 95 | 114 |
| 96 // Returns the Value type described in |schema|, or |default_type| if not found. | 115 // Returns the Value type described in |schema|, or |default_type| if not found. |
| 97 base::Value::Type GetType(const base::DictionaryValue* schema, | 116 base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema, |
| 98 base::Value::Type default_type) { | 117 base::Value::Type default_type) { |
| 99 // JSON-schema types to base::Value::Type mapping. | 118 // JSON-schema types to base::Value::Type mapping. |
| 100 static const struct { | 119 static const struct { |
| 101 // JSON schema type. | 120 // JSON schema type. |
| 102 const char* schema_type; | 121 const char* schema_type; |
| 103 // Correspondent value type. | 122 // Correspondent value type. |
| 104 base::Value::Type value_type; | 123 base::Value::Type value_type; |
| 105 } kSchemaToValueTypeMap[] = { | 124 } kSchemaToValueTypeMap[] = { |
| 106 { schema::kArray, base::Value::TYPE_LIST }, | 125 { schema::kArray, base::Value::TYPE_LIST }, |
| 107 { schema::kBoolean, base::Value::TYPE_BOOLEAN }, | 126 { schema::kBoolean, base::Value::TYPE_BOOLEAN }, |
| 108 { schema::kInteger, base::Value::TYPE_INTEGER }, | 127 { schema::kInteger, base::Value::TYPE_INTEGER }, |
| 109 { schema::kNull, base::Value::TYPE_NULL }, | 128 { schema::kNull, base::Value::TYPE_NULL }, |
| 110 { schema::kNumber, base::Value::TYPE_DOUBLE }, | 129 { schema::kNumber, base::Value::TYPE_DOUBLE }, |
| 111 { schema::kObject, base::Value::TYPE_DICTIONARY }, | 130 { schema::kObject, base::Value::TYPE_DICTIONARY }, |
| 112 { schema::kString, base::Value::TYPE_STRING }, | 131 { schema::kString, base::Value::TYPE_STRING }, |
| 113 }; | 132 }; |
| 114 | 133 |
| 115 if (!schema) | 134 if (!schema) |
| 116 return default_type; | 135 return default_type; |
| 117 std::string type; | 136 std::string type; |
| 118 if (!schema->GetString(schema::kType, &type)) | 137 if (!schema->GetStringWithoutPathExpansion(schema::kType, &type)) |
| 119 return default_type; | 138 return default_type; |
| 120 for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) { | 139 for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) { |
| 121 if (type == kSchemaToValueTypeMap[i].schema_type) | 140 if (type == kSchemaToValueTypeMap[i].schema_type) |
| 122 return kSchemaToValueTypeMap[i].value_type; | 141 return kSchemaToValueTypeMap[i].value_type; |
| 123 } | 142 } |
| 124 return default_type; | 143 return default_type; |
| 125 } | 144 } |
| 126 | 145 |
| 127 // Returns the default type for registry entries of |reg_type|, when there is | |
| 128 // no schema defined type for a policy. | |
| 129 base::Value::Type GetDefaultFor(DWORD reg_type) { | |
| 130 return reg_type == REG_DWORD ? base::Value::TYPE_INTEGER : | |
| 131 base::Value::TYPE_STRING; | |
| 132 } | |
| 133 | |
| 134 // Returns the entry with key |name| in |dictionary| (can be NULL), or NULL. | |
| 135 const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary, | |
| 136 const std::string& name) { | |
| 137 if (!dictionary) | |
| 138 return NULL; | |
| 139 const base::DictionaryValue* entry = NULL; | |
| 140 dictionary->GetDictionary(name, &entry); | |
| 141 return entry; | |
| 142 } | |
| 143 | |
| 144 // Returns the schema for property |name| given the |schema| of an object. | 146 // Returns the schema for property |name| given the |schema| of an object. |
| 145 // Returns the "additionalProperties" schema if no specific schema for | 147 // Returns the "additionalProperties" schema if no specific schema for |
| 146 // |name| is present. Returns NULL if no schema is found. | 148 // |name| is present. Returns NULL if no schema is found. |
| 147 const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema, | 149 const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema, |
| 148 const std::string& name) { | 150 const std::string& name) { |
| 149 const base::DictionaryValue* properties = | 151 const base::DictionaryValue* properties = |
| 150 GetEntry(schema, schema::kProperties); | 152 GetEntry(schema, schema::kProperties); |
| 151 const base::DictionaryValue* sub_schema = GetEntry(properties, name); | 153 const base::DictionaryValue* sub_schema = GetEntry(properties, name); |
| 152 if (sub_schema) | 154 if (sub_schema) |
| 153 return sub_schema; | 155 return sub_schema; |
| 154 // "additionalProperties" can be a boolean, but that case is ignored. | 156 // "additionalProperties" can be a boolean, but that case is ignored. |
| 155 return GetEntry(schema, schema::kAdditionalProperties); | 157 return GetEntry(schema, schema::kAdditionalProperties); |
| 156 } | 158 } |
| 157 | 159 |
| 158 // Converts string |value| to another |type|, if possible. | 160 // Reads the subtree of the Windows registry at |root| into the passed |dict|. |
| 159 base::Value* ConvertStringValue(const string16& value, base::Value::Type type) { | 161 void ReadRegistry(HKEY hive, |
| 160 switch (type) { | 162 const std::wstring& root, |
| 161 case base::Value::TYPE_NULL: | 163 base::DictionaryValue* dict) { |
| 162 return base::Value::CreateNullValue(); | 164 // Open the key. |
| 163 | 165 RegKey key(hive, root.c_str(), KEY_READ); |
| 166 if (!key.Valid()) | |
| 167 return; | |
| 168 | |
| 169 // First, read all the values of the key. | |
| 170 for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) { | |
| 171 const std::string name = UTF16ToUTF8(it.Name()); | |
| 172 DWORD type; | |
| 173 if (key.ReadValue(it.Name(), NULL, 0, &type) == ERROR_SUCCESS) { | |
| 174 switch (type) { | |
| 175 case REG_SZ: | |
| 176 case REG_EXPAND_SZ: { | |
| 177 std::wstring wstring_value; | |
| 178 if (key.ReadValue(it.Name(), &wstring_value) == ERROR_SUCCESS) { | |
|
Joao da Silva
2013/04/05 13:55:01
ReadValue() internally limits the value size to 10
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Good point. Instead of reviving ReadRegistryString
| |
| 179 dict->SetStringWithoutPathExpansion(name, | |
| 180 UTF16ToUTF8(wstring_value)); | |
| 181 continue; | |
| 182 } | |
| 183 break; | |
| 184 } | |
| 185 case REG_DWORD_LITTLE_ENDIAN: | |
| 186 case REG_DWORD_BIG_ENDIAN: { | |
| 187 DWORD dword_value = 0; | |
| 188 if (key.ReadValueDW(it.Name(), &dword_value) == ERROR_SUCCESS) { | |
| 189 if (type == REG_DWORD_BIG_ENDIAN) | |
| 190 dword_value = base::NetToHost32(dword_value); | |
| 191 else | |
| 192 dword_value = base::ByteSwapToLE32(dword_value); | |
| 193 dict->SetIntegerWithoutPathExpansion(name, dword_value); | |
| 194 continue; | |
| 195 } | |
| 196 break; | |
| 197 } | |
| 198 case REG_NONE: | |
| 199 case REG_LINK: | |
| 200 case REG_MULTI_SZ: | |
| 201 case REG_RESOURCE_LIST: | |
| 202 case REG_FULL_RESOURCE_DESCRIPTOR: | |
| 203 case REG_RESOURCE_REQUIREMENTS_LIST: | |
| 204 case REG_QWORD_LITTLE_ENDIAN: | |
| 205 // Unsupported type, message gets logged below. | |
| 206 break; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 LOG(WARNING) << "Failed to read hive " << hive << " at " | |
| 211 << root << kPathSep << name | |
| 212 << " type " << type; | |
| 213 } | |
| 214 | |
| 215 // Recurse for all subkeys. | |
| 216 for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) { | |
| 217 std::string name(UTF16ToUTF8(it.Name())); | |
| 218 if (dict->HasKey(name)) { | |
| 219 DLOG(WARNING) << "Ignoring registry key because a value exists with the " | |
| 220 "same name: " << root << kPathSep << name; | |
| 221 } else { | |
| 222 scoped_ptr<base::DictionaryValue> subdict(new base::DictionaryValue()); | |
| 223 ReadRegistry(hive, root + kPathSep + it.Name(), subdict.get()); | |
| 224 dict->SetWithoutPathExpansion(name, subdict.release()); | |
| 225 } | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 // Converts |value| in raw GPO representation to the internal policy value, as | |
| 230 // described by |schema|. This maps the ambiguous GPO data types to the | |
| 231 // internal policy value representations. | |
| 232 scoped_ptr<base::Value> ConvertPolicyValue( | |
| 233 const base::Value& value, | |
| 234 const base::DictionaryValue* schema) { | |
| 235 // Figure out the type to convert to from the schema. | |
| 236 const base::Value::Type result_type( | |
| 237 GetValueTypeForSchema(schema, value.GetType())); | |
| 238 | |
| 239 // If the type is good already, go with it. | |
| 240 if (value.IsType(result_type)) { | |
| 241 // Recurse for complex types if there is a schema. | |
| 242 if (schema) { | |
| 243 const base::DictionaryValue* dict = NULL; | |
| 244 const base::ListValue* list = NULL; | |
| 245 if (value.GetAsDictionary(&dict)) { | |
| 246 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); | |
| 247 for (base::DictionaryValue::Iterator entry(*dict); entry.HasNext(); | |
| 248 entry.Advance()) { | |
| 249 scoped_ptr<base::Value> converted_value( | |
| 250 ConvertPolicyValue(entry.value(), | |
| 251 GetSchemaFor(schema, entry.key()))); | |
| 252 result->Set(entry.key(), converted_value.release()); | |
| 253 } | |
| 254 return result.Pass(); | |
| 255 } else if (value.GetAsList(&list)) { | |
| 256 scoped_ptr<base::ListValue> result(new base::ListValue()); | |
| 257 for (base::ListValue::const_iterator entry(list->begin()); | |
| 258 entry != list->end(); ++entry) { | |
| 259 scoped_ptr<base::Value> converted_value( | |
| 260 ConvertPolicyValue(**entry, GetEntry(schema, schema::kItems))); | |
|
Joao da Silva
2013/04/05 13:55:01
The GetEntry() call can be outside the loop
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 261 result->Append(converted_value.release()); | |
| 262 } | |
| 263 return result.Pass(); | |
| 264 } | |
| 265 } | |
| 266 return make_scoped_ptr(value.DeepCopy()); | |
| 267 } | |
| 268 | |
| 269 // Else, do some conversions to map windows registry data types to JSON types. | |
| 270 std::string string_value; | |
| 271 int int_value = 0; | |
| 272 switch (result_type) { | |
| 273 case base::Value::TYPE_NULL: { | |
| 274 return make_scoped_ptr(base::Value::CreateNullValue()); | |
| 275 } | |
| 164 case base::Value::TYPE_BOOLEAN: { | 276 case base::Value::TYPE_BOOLEAN: { |
| 165 int int_value; | 277 // Accept booleans encoded as either string or integer. |
| 166 if (base::StringToInt(value, &int_value)) | 278 if (value.GetAsInteger(&int_value) || |
| 167 return base::Value::CreateBooleanValue(int_value != 0); | 279 (value.GetAsString(&string_value) && |
| 168 return NULL; | 280 base::StringToInt(string_value, &int_value))) { |
| 169 } | 281 return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0)); |
| 170 | 282 } |
| 283 break; | |
| 284 } | |
| 171 case base::Value::TYPE_INTEGER: { | 285 case base::Value::TYPE_INTEGER: { |
| 172 int int_value; | 286 // Integers may be string-encoded. |
| 173 if (base::StringToInt(value, &int_value)) | 287 if (value.GetAsString(&string_value) && |
| 174 return base::Value::CreateIntegerValue(int_value); | 288 base::StringToInt(string_value, &int_value)) { |
| 175 return NULL; | 289 return make_scoped_ptr(base::Value::CreateIntegerValue(int_value)); |
| 176 } | 290 } |
| 177 | 291 break; |
| 292 } | |
| 178 case base::Value::TYPE_DOUBLE: { | 293 case base::Value::TYPE_DOUBLE: { |
| 179 double double_value; | 294 // Doubles may be string-encoded or integer-encoded. |
| 180 if (base::StringToDouble(UTF16ToUTF8(value), &double_value)) | 295 double double_value = 0; |
| 181 return base::Value::CreateDoubleValue(double_value); | 296 if (value.GetAsInteger(&int_value)) { |
| 182 DLOG(WARNING) << "Failed to read policy value as double: " << value; | 297 return make_scoped_ptr(base::Value::CreateDoubleValue(int_value)); |
| 183 return NULL; | 298 } else if (value.GetAsString(&string_value) && |
| 184 } | 299 base::StringToDouble(string_value, &double_value)) { |
| 185 | 300 return make_scoped_ptr(base::Value::CreateDoubleValue(double_value)); |
| 186 case base::Value::TYPE_STRING: | 301 } |
| 187 return base::Value::CreateStringValue(value); | 302 break; |
| 188 | 303 } |
| 189 case base::Value::TYPE_DICTIONARY: | 304 case base::Value::TYPE_LIST: { |
| 190 case base::Value::TYPE_LIST: | 305 // Lists are encoded as subkeys with numbered value in the registry. |
| 191 return base::JSONReader::Read(UTF16ToUTF8(value)); | 306 const base::DictionaryValue* dict = NULL; |
| 192 | 307 if (value.GetAsDictionary(&dict)) { |
| 193 case base::Value::TYPE_BINARY: | 308 scoped_ptr<base::ListValue> result(new base::ListValue()); |
| 194 DLOG(WARNING) << "Cannot convert REG_SZ entry to type " << type; | 309 for (int i = 1; ; ++i) { |
| 195 return NULL; | 310 const base::Value* entry = NULL; |
| 196 } | 311 if (!dict->Get(base::IntToString(i), &entry)) |
| 197 NOTREACHED(); | 312 break; |
| 198 return NULL; | 313 scoped_ptr<base::Value> converted_value( |
| 199 } | 314 ConvertPolicyValue(*entry, GetEntry(schema, schema::kItems))); |
|
Joao da Silva
2013/04/05 13:55:01
GetEntry() can be outside the loop
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 200 | 315 result->Append(converted_value.release()); |
| 201 // Converts an integer |value| to another |type|, if possible. | 316 } |
| 202 base::Value* ConvertIntegerValue(uint32 value, base::Value::Type type) { | 317 return result.Pass(); |
| 203 switch (type) { | 318 } |
| 204 case base::Value::TYPE_BOOLEAN: | 319 // Fall through in order to accept lists encoded as JSON strings. |
| 205 return base::Value::CreateBooleanValue(value != 0); | 320 } |
| 206 | 321 case base::Value::TYPE_DICTIONARY: { |
| 207 case base::Value::TYPE_INTEGER: | 322 // Dictionaries may be encoded as JSON strings. |
| 208 return base::Value::CreateIntegerValue(value); | 323 if (value.GetAsString(&string_value)) { |
| 209 | 324 scoped_ptr<base::Value> result(base::JSONReader::Read(string_value)); |
| 210 case base::Value::TYPE_DOUBLE: | 325 if (result && result->IsType(result_type)) |
| 211 return base::Value::CreateDoubleValue(value); | 326 return result.Pass(); |
| 212 | 327 } |
| 213 case base::Value::TYPE_NULL: | 328 break; |
| 329 } | |
| 214 case base::Value::TYPE_STRING: | 330 case base::Value::TYPE_STRING: |
| 215 case base::Value::TYPE_BINARY: | 331 case base::Value::TYPE_BINARY: |
| 216 case base::Value::TYPE_DICTIONARY: | 332 // No conversion possible. |
| 217 case base::Value::TYPE_LIST: | 333 break; |
| 218 DLOG(WARNING) << "Cannot convert REG_DWORD entry to type " << type; | 334 } |
| 219 return NULL; | 335 |
| 220 } | 336 LOG(WARNING) << "Failed to convert " << value.GetType() |
| 221 NOTREACHED(); | 337 << " to " << result_type; |
|
Joao da Silva
2013/04/05 13:55:01
nit: indent
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 222 return NULL; | 338 return make_scoped_ptr(base::Value::CreateNullValue()); |
| 223 } | 339 } |
| 224 | 340 |
| 225 // Reads a value from the registry |key| named |name| with registry type | 341 // Parses |gpo_dict| according to |schema| and writes the resulting policy |
| 226 // |registry_type| as a value of type |type|. | 342 // settings to |policy| for the given |scope| and |level|. |
| 227 // Returns NULL if the value could not be loaded or converted. | 343 void ParsePolicy(const base::DictionaryValue* gpo_dict, |
| 228 base::Value* ReadPolicyValue(RegKey* key, | 344 PolicyLevel level, |
| 229 const string16& name, | 345 PolicyScope scope, |
| 230 DWORD registry_type, | 346 const base::DictionaryValue* schema, |
| 231 base::Value::Type type) { | 347 PolicyMap* policy) { |
| 232 switch (registry_type) { | 348 if (!gpo_dict) |
| 233 case REG_SZ: { | 349 return; |
| 234 string16 value; | 350 |
| 235 if (ReadRegistryString(key, name, &value)) | 351 scoped_ptr<base::Value> policy_value(ConvertPolicyValue(*gpo_dict, schema)); |
| 236 return ConvertStringValue(value, type); | 352 const base::DictionaryValue* policy_dict = NULL; |
| 237 break; | 353 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { |
| 238 } | 354 LOG(WARNING) << "Root policy object is not a dictionary!"; |
| 239 | 355 return; |
| 240 case REG_DWORD: { | 356 } |
| 241 uint32 value; | 357 |
| 242 if (ReadRegistryInteger(key, name, &value)) | 358 for (base::DictionaryValue::Iterator it(*policy_dict); it.HasNext(); |
| 243 return ConvertIntegerValue(value, type); | 359 it.Advance()) { |
| 244 break; | 360 policy->Set(it.key(), level, scope, it.value().DeepCopy()); |
| 245 } | 361 } |
|
Joao da Silva
2013/04/05 13:55:01
policy->LoadFrom(policy_dict, level, scope)
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 246 | 362 } |
| 247 default: | 363 |
| 248 DLOG(WARNING) << "Registry type not supported for key " << name; | 364 // Queries Windows for applied group policy and write the result to |policy|. |
| 249 break; | 365 // This is the preferred way to obtain GPO data, there are reports of abuse |
| 250 } | 366 // of the registry GPO keys by 3rd-party software. |
| 251 return NULL; | 367 bool ReadPolicyFromGPO(PolicyScope scope, base::DictionaryValue* policy) { |
| 252 } | 368 PGROUP_POLICY_OBJECT policy_object_list_pointer = NULL; |
| 253 | 369 DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0; |
| 254 // Forward declaration for ReadComponentListValue(). | 370 if (GetAppliedGPOList(flags, NULL, NULL, &kRegistrySettingsCSEGUID, |
| 255 base::DictionaryValue* ReadComponentDictionaryValue( | 371 &policy_object_list_pointer)) { |
| 256 HKEY hive, | 372 PLOG(ERROR) << "GetAppliedGPOList for scope " << scope << " failed"; |
| 257 const string16& path, | 373 return false; |
|
Joao da Silva
2013/04/05 13:55:01
What happens in a machine without any policy? Does
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Yes, I saw that happening in a pristine VM.
| |
| 258 const base::DictionaryValue* schema); | 374 } |
| 259 | 375 scoped_ptr<GROUP_POLICY_OBJECT, FreeGPOListDeleter> policy_object_list( |
| 260 // Loads the list at |path| in the given |hive|. |schema| is a JSON schema | 376 policy_object_list_pointer); |
| 261 // (http://json-schema.org/) that describes the expected type of the list. | 377 |
| 262 // Ownership of the result is transferred to the caller. | 378 base::DictionaryValue regular_policy; |
| 263 base::ListValue* ReadComponentListValue(HKEY hive, | 379 base::DictionaryValue forced_policy; |
| 264 const string16& path, | 380 for (GROUP_POLICY_OBJECT* policy_object = policy_object_list.get(); |
| 265 const base::DictionaryValue* schema) { | 381 policy_object; policy_object = policy_object->pNext) { |
| 266 // The sub-elements are indexed from 1 to N. They can be represented as | 382 /* |
| 267 // registry values or registry keys though; use |schema| first to try to | 383 LOG(ERROR) << "GPO object: " << std::endl |
| 268 // determine the right type, and if that fails default to STRING. | 384 << "options " << policy_object->dwOptions << std::endl |
| 269 | 385 << "version " << policy_object->dwVersion << std::endl |
| 270 RegKey key(hive, path.c_str(), KEY_READ); | 386 << "dspath " << policy_object->lpDSPath << std::endl |
| 271 if (!key.Valid()) | 387 << "lpFileSysPath " << policy_object->lpFileSysPath << std::endl |
| 272 return NULL; | 388 << "lpDisplayName " << policy_object->lpDisplayName << std::endl |
| 273 | 389 << "GPOName " << policy_object->szGPOName << std::endl |
| 274 // Get the schema for list items. | 390 << "GPOLink " << policy_object->GPOLink << std::endl |
| 275 schema = GetEntry(schema, schema::kItems); | 391 << "lParam " << policy_object->lParam << std::endl |
| 276 base::Value::Type type = GetType(schema, base::Value::TYPE_STRING); | 392 << "next " << policy_object->pNext << std::endl |
| 277 base::ListValue* list = new base::ListValue(); | 393 << "prev " << policy_object->pPrev << std::endl |
| 278 for (int i = 1; ; ++i) { | 394 << "lpExtensions " << policy_object->lpExtensions << std::endl |
| 279 string16 name = base::IntToString16(i); | 395 << "lParam2 " << policy_object->lParam2 << std::endl |
| 280 base::Value* value = NULL; | 396 << "lpLink " << policy_object->lpLink << std::endl; |
|
Joao da Silva
2013/04/05 13:55:01
You've been having some fun eh :-)
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 281 if (type == base::Value::TYPE_DICTIONARY) { | 397 */ |
| 282 value = | 398 |
| 283 ReadComponentDictionaryValue(hive, path + kPathSep + name, schema); | 399 if (policy_object->dwOptions & GPO_FLAG_DISABLE) |
|
Joao da Silva
2013/04/05 13:55:01
It's not clear to me if this flag is set for boole
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
This refers to the entire GroupPolicyObject, not i
| |
| 284 } else if (type == base::Value::TYPE_LIST) { | 400 continue; |
| 285 value = ReadComponentListValue(hive, path + kPathSep + name, schema); | 401 |
| 402 if (PathIsUNC(policy_object->lpFileSysPath)) { | |
|
Joao da Silva
2013/04/05 13:55:01
Can policy_object->lpDSPath have more policy that
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
I've actually looked at the DS portion, there's on
| |
| 403 // UNC path: Assume this is an AD-managed machine, which updates the | |
| 404 // registry via GPO's standard registry CSE periodically. Fall back to | |
| 405 // reading from the registry in this case. | |
| 406 return false; | |
| 407 } | |
| 408 | |
| 409 base::FilePath preg_file_path( | |
| 410 base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName)); | |
| 411 if (policy_object->dwOptions & GPO_FLAG_FORCE) { | |
| 412 base::DictionaryValue new_forced_policy; | |
| 413 if (!preg_parser::ReadFile(preg_file_path, &new_forced_policy)) | |
|
Joao da Silva
2013/04/05 13:55:01
This is going to read all the policies for all the
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
I had done that already, but not updated the uploa
| |
| 414 return false; | |
| 415 | |
| 416 // Merge with existing forced policy, giving precedence to the existing | |
| 417 // forced policy. | |
| 418 new_forced_policy.MergeDictionary(&forced_policy); | |
| 419 forced_policy.Swap(&new_forced_policy); | |
| 286 } else { | 420 } else { |
| 287 DWORD reg_type; | 421 if (!preg_parser::ReadFile(preg_file_path, ®ular_policy)) |
| 288 key.ReadValue(name.c_str(), NULL, NULL, ®_type); | 422 return false; |
| 289 if (reg_type != REG_NONE) | 423 } |
| 290 value = ReadPolicyValue(&key, name, reg_type, type); | 424 } |
| 291 } | 425 |
| 292 if (value) | 426 // Construct the resulting policy data dictionary. |
| 293 list->Append(value); | 427 base::DictionaryValue gpo_policy; |
| 294 else | 428 gpo_policy.MergeDictionary(®ular_policy); |
|
Joao da Silva
2013/04/05 13:55:01
Swap?
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
Done.
| |
| 295 break; | 429 gpo_policy.MergeDictionary(&forced_policy); |
| 296 } | 430 |
| 297 return list; | 431 // Extract the Chrome-specific part. |
| 298 } | 432 std::string path; |
| 299 | 433 ReplaceChars(UTF16ToUTF8(kRegistryChromePolicyKey), "\\", ".", &path); |
| 300 // Loads the dictionary at |path| in the given |hive|. |schema| is a JSON | 434 base::DictionaryValue* chrome_policy; |
| 301 // schema (http://json-schema.org/) that describes the expected types for the | 435 if (gpo_policy.GetDictionary(path, &chrome_policy)) |
| 302 // dictionary entries. When the type for a certain entry isn't described in the | 436 policy->Swap(chrome_policy); |
| 303 // schema, a default conversion takes place. |schema| can be NULL. | 437 |
| 304 // Ownership of the result is transferred to the caller. | 438 return true; |
| 305 base::DictionaryValue* ReadComponentDictionaryValue( | |
| 306 HKEY hive, | |
| 307 const string16& path, | |
| 308 const base::DictionaryValue* schema) { | |
| 309 // A "value" in the registry is like a file in a filesystem, and a "key" is | |
| 310 // like a directory, that contains other "values" and "keys". | |
| 311 // Unfortunately it is possible to have a name both as a "value" and a "key". | |
| 312 // In those cases, the sub "key" will be ignored; this choice is arbitrary. | |
| 313 | |
| 314 // First iterate over all the "values" in |path| and convert them; then | |
| 315 // recurse into each "key" in |path| and convert them as dictionaries. | |
| 316 | |
| 317 RegKey key(hive, path.c_str(), KEY_READ); | |
| 318 if (!key.Valid()) | |
| 319 return NULL; | |
| 320 | |
| 321 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 322 for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) { | |
| 323 string16 name16(it.Name()); | |
| 324 std::string name(UTF16ToUTF8(name16)); | |
| 325 const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name); | |
| 326 base::Value::Type type = GetType(sub_schema, GetDefaultFor(it.Type())); | |
| 327 base::Value* value = ReadPolicyValue(&key, name16, it.Type(), type); | |
| 328 if (value) | |
| 329 dict->Set(name, value); | |
| 330 } | |
| 331 | |
| 332 for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) { | |
| 333 string16 name16(it.Name()); | |
| 334 std::string name(UTF16ToUTF8(name16)); | |
| 335 if (dict->HasKey(name)) { | |
| 336 DLOG(WARNING) << "Ignoring registry key because a value exists with the " | |
| 337 "same name: " << path << kPathSep << name; | |
| 338 continue; | |
| 339 } | |
| 340 | |
| 341 const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name); | |
| 342 base::Value::Type type = GetType(sub_schema, base::Value::TYPE_DICTIONARY); | |
| 343 base::Value* value = NULL; | |
| 344 const string16 sub_path = path + kPathSep + name16; | |
| 345 if (type == base::Value::TYPE_DICTIONARY) { | |
| 346 value = ReadComponentDictionaryValue(hive, sub_path, sub_schema); | |
| 347 } else if (type == base::Value::TYPE_LIST) { | |
| 348 value = ReadComponentListValue(hive, sub_path, sub_schema); | |
| 349 } else { | |
| 350 DLOG(WARNING) << "Can't read a simple type in registry key at " << path; | |
| 351 } | |
| 352 if (value) | |
| 353 dict->Set(name, value); | |
| 354 } | |
| 355 | |
| 356 return dict; | |
| 357 } | |
| 358 | |
| 359 // Reads a JSON schema from the given |registry_value|, at the given | |
| 360 // |registry_key| in |hive|. |registry_value| must be a string (REG_SZ), and | |
| 361 // is decoded as JSON data. Returns NULL on failure. Ownership is transferred | |
| 362 // to the caller. | |
| 363 base::DictionaryValue* ReadRegistrySchema(HKEY hive, | |
| 364 const string16& registry_key, | |
| 365 const string16& registry_value) { | |
| 366 RegKey key(hive, registry_key.c_str(), KEY_READ); | |
| 367 string16 schema; | |
| 368 if (!ReadRegistryString(&key, registry_value, &schema)) | |
| 369 return NULL; | |
| 370 // A JSON schema is represented in JSON too. | |
| 371 scoped_ptr<base::Value> value(base::JSONReader::Read(UTF16ToUTF8(schema))); | |
| 372 if (!value.get()) | |
| 373 return NULL; | |
| 374 base::DictionaryValue* dict = NULL; | |
| 375 if (!value->GetAsDictionary(&dict)) | |
| 376 return NULL; | |
| 377 // The top-level entry must be an object, and each of its properties maps | |
| 378 // a policy name to its schema. | |
| 379 if (GetType(dict, base::Value::TYPE_DICTIONARY) != | |
| 380 base::Value::TYPE_DICTIONARY) { | |
| 381 DLOG(WARNING) << "schema top-level type isn't \"object\""; | |
| 382 return NULL; | |
| 383 } | |
| 384 value.release(); | |
| 385 return dict; | |
| 386 } | 439 } |
| 387 | 440 |
| 388 } // namespace | 441 } // namespace |
| 389 | 442 |
| 390 PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list) | 443 PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list) |
| 391 : is_initialized_(false), | 444 : is_initialized_(false), |
| 392 policy_list_(policy_list), | 445 policy_list_(policy_list), |
| 393 user_policy_changed_event_(false, false), | 446 user_policy_changed_event_(false, false), |
| 394 machine_policy_changed_event_(false, false), | 447 machine_policy_changed_event_(false, false), |
| 395 user_policy_watcher_failed_(false), | 448 user_policy_watcher_failed_(false), |
| 396 machine_policy_watcher_failed_(false) { | 449 machine_policy_watcher_failed_(false) { |
| 397 if (!RegisterGPNotification(user_policy_changed_event_.handle(), false)) { | 450 if (!RegisterGPNotification(user_policy_changed_event_.handle(), false)) { |
| 398 DPLOG(WARNING) << "Failed to register user group policy notification"; | 451 DPLOG(WARNING) << "Failed to register user group policy notification"; |
| 399 user_policy_watcher_failed_ = true; | 452 user_policy_watcher_failed_ = true; |
| 400 } | 453 } |
| 401 if (!RegisterGPNotification(machine_policy_changed_event_.handle(), true)) { | 454 if (!RegisterGPNotification(machine_policy_changed_event_.handle(), true)) { |
| 402 DPLOG(WARNING) << "Failed to register machine group policy notification."; | 455 DPLOG(WARNING) << "Failed to register machine group policy notification."; |
| 403 machine_policy_watcher_failed_ = true; | 456 machine_policy_watcher_failed_ = true; |
| 404 } | 457 } |
| 405 } | 458 } |
| 406 | 459 |
| 407 PolicyLoaderWin::~PolicyLoaderWin() { | 460 PolicyLoaderWin::~PolicyLoaderWin() { |
| 408 user_policy_watcher_.StopWatching(); | 461 user_policy_watcher_.StopWatching(); |
| 409 machine_policy_watcher_.StopWatching(); | 462 machine_policy_watcher_.StopWatching(); |
| 410 } | 463 } |
| 411 | 464 |
| 412 void PolicyLoaderWin::InitOnFile() { | 465 void PolicyLoaderWin::InitOnFile() { |
| 466 // Build the Chrome policy schema. | |
| 467 scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue()); | |
| 468 for (const PolicyDefinitionList::Entry* e = policy_list_->begin; | |
| 469 e != policy_list_->end; ++e) { | |
| 470 const std::string schema_type = GetSchemaTypeForValueType(e->value_type); | |
| 471 scoped_ptr<base::DictionaryValue> entry_schema(new base::DictionaryValue()); | |
| 472 entry_schema->SetStringWithoutPathExpansion(json_schema_constants::kType, | |
| 473 schema_type); | |
| 474 | |
| 475 if (e->value_type == base::Value::TYPE_LIST) { | |
| 476 scoped_ptr<base::DictionaryValue> items_schema( | |
| 477 new base::DictionaryValue()); | |
| 478 items_schema->SetStringWithoutPathExpansion( | |
| 479 json_schema_constants::kType, json_schema_constants::kString); | |
| 480 entry_schema->SetWithoutPathExpansion(json_schema_constants::kItems, | |
| 481 items_schema.release()); | |
| 482 } | |
| 483 properties->SetWithoutPathExpansion(e->name, entry_schema.release()); | |
| 484 } | |
| 485 chrome_policy_schema_.SetStringWithoutPathExpansion( | |
| 486 json_schema_constants::kType, json_schema_constants::kObject); | |
| 487 chrome_policy_schema_.SetWithoutPathExpansion( | |
| 488 json_schema_constants::kProperties, properties.release()); | |
|
Joao da Silva
2013/04/05 13:55:01
Using a DictionaryBuilder here may make this clean
Mattias Nissler (ping if slow)
2013/04/09 22:43:38
DictionaryBuilder is still in chrome/common/extens
| |
| 489 | |
| 413 is_initialized_ = true; | 490 is_initialized_ = true; |
| 414 SetupWatches(); | 491 SetupWatches(); |
| 415 } | 492 } |
| 416 | 493 |
| 417 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { | 494 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { |
| 418 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); | |
| 419 LoadChromePolicy( | |
| 420 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))); | |
| 421 Load3rdPartyPolicies(bundle.get()); | |
| 422 return bundle.Pass(); | |
| 423 } | |
| 424 | |
| 425 void PolicyLoaderWin::LoadChromePolicy(PolicyMap* chrome_policies) { | |
| 426 // Reset the watches BEFORE reading the individual policies to avoid | 495 // Reset the watches BEFORE reading the individual policies to avoid |
| 427 // missing a change notification. | 496 // missing a change notification. |
| 428 if (is_initialized_) | 497 if (is_initialized_) |
| 429 SetupWatches(); | 498 SetupWatches(); |
| 430 | 499 |
| 431 // |kKeyPaths| is in decreasing order of priority. | 500 // Policy scope and corresponding hive. |
| 432 static const struct { | 501 static const struct { |
| 433 const wchar_t* path; | 502 PolicyScope scope; |
| 434 PolicyLevel level; | 503 HKEY hive; |
| 435 } kKeyPaths[] = { | 504 } kScopes[] = { |
| 436 { kRegistryMandatorySubKey, POLICY_LEVEL_MANDATORY }, | 505 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, |
| 437 { kRegistryRecommendedSubKey, POLICY_LEVEL_RECOMMENDED }, | 506 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, |
| 438 }; | 507 }; |
| 439 | 508 |
| 440 // Lookup at the mandatory path for both user and machine policies first, and | 509 // Load policy data for the different scopes/levels and merge them. |
| 441 // then at the recommended path. | 510 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); |
| 442 for (size_t k = 0; k < arraysize(kKeyPaths); ++k) { | 511 PolicyMap* chrome_policy = |
| 443 for (size_t h = 0; h < arraysize(kHives); ++h) { | 512 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); |
| 444 // Iterate over keys and values at this hive and path. | 513 for (size_t i = 0; i < arraysize(kScopes); ++i) { |
| 445 HKEY hive = kHives[h].hive; | 514 PolicyScope scope = kScopes[i].scope; |
| 446 string16 path(kKeyPaths[k].path); | 515 base::DictionaryValue gpo_dict; |
| 447 RegKey key; | 516 if (!ReadPolicyFromGPO(scope, &gpo_dict)) |
| 448 if (key.Open(hive, path.c_str(), KEY_READ) != ERROR_SUCCESS || | 517 ReadRegistry(kScopes[i].hive, kRegistryChromePolicyKey, &gpo_dict); |
| 449 !key.Valid()) { | 518 |
| 519 // Remove special-cased entries from the GPO dictionary. | |
| 520 base::DictionaryValue* temp_dict = NULL; | |
| 521 scoped_ptr<base::DictionaryValue> recommended_dict( | |
| 522 RemoveDict(&gpo_dict, kKeyRecommended)); | |
| 523 scoped_ptr<base::DictionaryValue> third_party_dict( | |
| 524 RemoveDict(&gpo_dict, kKeyThirdParty)); | |
| 525 | |
| 526 // Load Chrome policy. | |
| 527 LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy); | |
| 528 LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope, | |
| 529 chrome_policy); | |
| 530 | |
| 531 // Load 3rd-party policy. | |
| 532 if (third_party_dict) | |
| 533 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); | |
| 534 } | |
| 535 | |
| 536 return bundle.Pass(); | |
| 537 } | |
| 538 | |
| 539 void PolicyLoaderWin::LoadChromePolicy(const base::DictionaryValue* gpo_dict, | |
| 540 PolicyLevel level, | |
| 541 PolicyScope scope, | |
| 542 PolicyMap* chrome_policy_map) { | |
| 543 PolicyMap policy; | |
| 544 ParsePolicy(gpo_dict, level, scope, &chrome_policy_schema_, &policy); | |
| 545 chrome_policy_map->MergeFrom(policy); | |
| 546 } | |
| 547 | |
| 548 void PolicyLoaderWin::Load3rdPartyPolicy( | |
| 549 const DictionaryValue* gpo_dict, | |
| 550 PolicyScope scope, | |
| 551 PolicyBundle* bundle) { | |
| 552 // Map of known 3rd party policy domain name to their enum values. | |
| 553 static const struct { | |
| 554 const char* name; | |
| 555 PolicyDomain domain; | |
| 556 } k3rdPartyDomains[] = { | |
| 557 { "extensions", POLICY_DOMAIN_EXTENSIONS }, | |
| 558 }; | |
| 559 | |
| 560 // Policy level and corresponding path. | |
| 561 static const struct { | |
| 562 PolicyLevel level; | |
| 563 const char* path; | |
| 564 } kLevels[] = { | |
| 565 { POLICY_LEVEL_MANDATORY, kKeyMandatory }, | |
| 566 { POLICY_LEVEL_RECOMMENDED, kKeyRecommended }, | |
| 567 }; | |
| 568 | |
| 569 for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) { | |
| 570 const char* name = k3rdPartyDomains[i].name; | |
| 571 const PolicyDomain domain = k3rdPartyDomains[i].domain; | |
| 572 const base::DictionaryValue* domain_dict = NULL; | |
| 573 if (!gpo_dict->GetDictionaryWithoutPathExpansion(name, &domain_dict) || | |
| 574 !domain_dict) { | |
| 575 continue; | |
| 576 } | |
| 577 | |
| 578 for (base::DictionaryValue::Iterator component(*domain_dict); | |
| 579 component.HasNext(); component.Advance()) { | |
| 580 const base::DictionaryValue* component_dict = NULL; | |
| 581 if (!component.value().GetAsDictionary(&component_dict) || | |
| 582 !component_dict) { | |
| 450 continue; | 583 continue; |
| 451 } | 584 } |
| 452 | 585 |
| 453 // Iterate over values for most policies. | 586 // Load the schema. |
| 454 for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) { | 587 scoped_ptr<base::Value> schema; |
| 455 std::string name(UTF16ToUTF8(it.Name())); | 588 const base::DictionaryValue* schema_dict = NULL; |
| 456 // Skip if a higher-priority policy value was already inserted, or | 589 std::string schema_json; |
| 457 // if this is the default value (empty string). | 590 if (component_dict->GetStringWithoutPathExpansion(kKeySchema, |
| 458 if (chrome_policies->Get(name) || name.empty()) | 591 &schema_json)) { |
| 459 continue; | 592 schema.reset(base::JSONReader::Read(schema_json)); |
| 460 // Get the expected policy type, if this is a known policy. | 593 if (!schema || !schema->GetAsDictionary(&schema_dict)) { |
| 461 base::Value::Type type = GetDefaultFor(it.Type()); | 594 LOG(WARNING) << "Failed to parse 3rd-part policy schema for " |
| 462 for (const PolicyDefinitionList::Entry* e = policy_list_->begin; | 595 << domain << "/" << component.key(); |
| 463 e != policy_list_->end; ++e) { | |
| 464 if (name == e->name) { | |
| 465 type = e->value_type; | |
| 466 break; | |
| 467 } | |
| 468 } | 596 } |
| 469 base::Value* value = ReadPolicyValue(&key, it.Name(), it.Type(), type); | |
| 470 if (!value) | |
| 471 value = base::Value::CreateNullValue(); | |
| 472 chrome_policies->Set(name, kKeyPaths[k].level, kHives[h].scope, value); | |
| 473 } | 597 } |
| 474 | 598 |
| 475 // Iterate over keys for policies of type string-list. | 599 // Parse policy. |
| 476 for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) { | 600 for (size_t j = 0; j < arraysize(kLevels); j++) { |
| 477 std::string name(UTF16ToUTF8(it.Name())); | 601 const base::DictionaryValue* policy_dict = NULL; |
| 478 // Skip if a higher-priority policy value was already inserted, or | 602 if (!component_dict->GetDictionaryWithoutPathExpansion( |
| 479 // if this is the 3rd party policy subkey. | 603 kLevels[j].path, &policy_dict) || |
| 480 const string16 kThirdParty16(kThirdParty); | 604 !policy_dict) { |
| 481 if (chrome_policies->Get(name) || it.Name() == kThirdParty16) | |
| 482 continue; | |
| 483 string16 list_path = path + kPathSep + it.Name(); | |
| 484 RegKey key; | |
| 485 if (key.Open(hive, list_path.c_str(), KEY_READ) != ERROR_SUCCESS || | |
| 486 !key.Valid()) { | |
| 487 continue; | 605 continue; |
| 488 } | 606 } |
| 489 base::ListValue* result = new base::ListValue(); | 607 |
| 490 string16 value; | 608 PolicyMap policy; |
| 491 int index = 0; | 609 ParsePolicy(policy_dict, kLevels[j].level, scope, schema_dict, &policy); |
| 492 while (ReadRegistryString(&key, base::IntToString16(++index), &value)) | 610 PolicyNamespace policy_namespace(domain, component.key()); |
| 493 result->Append(base::Value::CreateStringValue(value)); | 611 bundle->Get(policy_namespace).MergeFrom(policy); |
| 494 chrome_policies->Set(name, kKeyPaths[k].level, kHives[h].scope, result); | |
| 495 } | 612 } |
| 496 } | 613 } |
| 497 } | 614 } |
| 498 } | 615 } |
| 499 | |
| 500 void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) { | |
| 501 // Each 3rd party namespace can have policies on both HKLM and HKCU. They | |
| 502 // should be merged, giving priority to HKLM for policies with the same name. | |
| 503 | |
| 504 // Map of known domain name to their enum values. | |
| 505 static const struct { | |
| 506 const char* name; | |
| 507 PolicyDomain domain; | |
| 508 } kDomains[] = { | |
| 509 { "extensions", POLICY_DOMAIN_EXTENSIONS }, | |
| 510 }; | |
| 511 | |
| 512 // Map of policy paths to their corresponding policy level, in decreasing | |
| 513 // order of priority. | |
| 514 static const struct { | |
| 515 const char* path; | |
| 516 PolicyLevel level; | |
| 517 } kKeyPaths[] = { | |
| 518 { "policy", POLICY_LEVEL_MANDATORY }, | |
| 519 { "recommended", POLICY_LEVEL_RECOMMENDED }, | |
| 520 }; | |
| 521 | |
| 522 // Path where policies for components are stored. | |
| 523 const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep + | |
| 524 kThirdParty + kPathSep; | |
| 525 | |
| 526 for (size_t h = 0; h < arraysize(kHives); ++h) { | |
| 527 HKEY hkey = kHives[h].hive; | |
| 528 | |
| 529 for (size_t d = 0; d < arraysize(kDomains); ++d) { | |
| 530 // Each subkey under this domain is a component of that domain. | |
| 531 // |domain_path| == SOFTWARE\Policies\Chromium\3rdparty\<domain> | |
| 532 string16 domain_path = kPathPrefix + ASCIIToUTF16(kDomains[d].name); | |
| 533 | |
| 534 for (RegistryKeyIterator domain_iterator(hkey, domain_path.c_str()); | |
| 535 domain_iterator.Valid(); ++domain_iterator) { | |
| 536 string16 component(domain_iterator.Name()); | |
| 537 string16 component_path = domain_path + kPathSep + component; | |
| 538 | |
| 539 // Load the schema for this component's policy, if present. | |
| 540 scoped_ptr<base::DictionaryValue> schema( | |
| 541 ReadRegistrySchema(hkey, component_path, kSchema)); | |
| 542 | |
| 543 for (size_t k = 0; k < arraysize(kKeyPaths); ++k) { | |
| 544 string16 path = | |
| 545 component_path + kPathSep + ASCIIToUTF16(kKeyPaths[k].path); | |
| 546 | |
| 547 scoped_ptr<base::DictionaryValue> dictionary( | |
| 548 ReadComponentDictionaryValue(hkey, path, schema.get())); | |
| 549 if (dictionary.get()) { | |
| 550 PolicyMap policies; | |
| 551 policies.LoadFrom( | |
| 552 dictionary.get(), kKeyPaths[k].level, kHives[h].scope); | |
| 553 // LoadFrom() overwrites any existing values. Use a temporary map | |
| 554 // and then use MergeFrom(), that only overwrites values with lower | |
| 555 // priority. | |
| 556 bundle->Get(PolicyNamespace(kDomains[d].domain, | |
| 557 UTF16ToUTF8(component))) | |
| 558 .MergeFrom(policies); | |
| 559 } | |
| 560 } | |
| 561 } | |
| 562 } | |
| 563 } | |
| 564 } | |
| 565 | 616 |
| 566 void PolicyLoaderWin::SetupWatches() { | 617 void PolicyLoaderWin::SetupWatches() { |
| 567 DCHECK(is_initialized_); | 618 DCHECK(is_initialized_); |
| 568 if (!user_policy_watcher_failed_ && | 619 if (!user_policy_watcher_failed_ && |
| 569 !user_policy_watcher_.GetWatchedObject() && | 620 !user_policy_watcher_.GetWatchedObject() && |
| 570 !user_policy_watcher_.StartWatching( | 621 !user_policy_watcher_.StartWatching( |
| 571 user_policy_changed_event_.handle(), this)) { | 622 user_policy_changed_event_.handle(), this)) { |
| 572 DLOG(WARNING) << "Failed to start watch for user policy change event"; | 623 DLOG(WARNING) << "Failed to start watch for user policy change event"; |
| 573 user_policy_watcher_failed_ = true; | 624 user_policy_watcher_failed_ = true; |
| 574 } | 625 } |
| 575 if (!machine_policy_watcher_failed_ && | 626 if (!machine_policy_watcher_failed_ && |
| 576 !machine_policy_watcher_.GetWatchedObject() && | 627 !machine_policy_watcher_.GetWatchedObject() && |
| 577 !machine_policy_watcher_.StartWatching( | 628 !machine_policy_watcher_.StartWatching( |
| 578 machine_policy_changed_event_.handle(), this)) { | 629 machine_policy_changed_event_.handle(), this)) { |
| 579 DLOG(WARNING) << "Failed to start watch for machine policy change event"; | 630 DLOG(WARNING) << "Failed to start watch for machine policy change event"; |
| 580 machine_policy_watcher_failed_ = true; | 631 machine_policy_watcher_failed_ = true; |
| 581 } | 632 } |
| 582 } | 633 } |
| 583 | 634 |
| 584 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { | 635 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { |
| 585 DCHECK(object == user_policy_changed_event_.handle() || | 636 DCHECK(object == user_policy_changed_event_.handle() || |
| 586 object == machine_policy_changed_event_.handle()) | 637 object == machine_policy_changed_event_.handle()) |
| 587 << "unexpected object signaled policy reload, obj = " | 638 << "unexpected object signaled policy reload, obj = " |
| 588 << std::showbase << std::hex << object; | 639 << std::showbase << std::hex << object; |
| 589 Reload(false); | 640 Reload(false); |
| 590 } | 641 } |
| 591 | 642 |
| 592 } // namespace policy | 643 } // namespace policy |
| OLD | NEW |