Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Side by Side Diff: chrome/browser/policy/policy_loader_win.cc

Issue 13619014: Change PolicyLoaderWin to load PReg files if possible. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments, add unit tests. Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 #include <vector>
8 9
9 #include <string.h> 10 #include <rpc.h> // For struct GUID
11 #include <shlwapi.h> // For PathIsUNC()
Joao da Silva 2013/04/10 12:32:12 Suggestion: I find vertically aligned comments eas
Mattias Nissler (ping if slow) 2013/04/10 13:20:25 With my nitpick hat on, I'd agree :) But being hon
12 #include <windows.h>
10 13
11 #include <userenv.h> 14 // shlwapi.dll is required for PathIsUNC().
12 15 #pragma comment(lib, "shlwapi.lib")
13 // userenv.dll is required for RegisterGPNotification(). 16 // userenv.dll is required for various GPO functions.
14 #pragma comment(lib, "userenv.lib") 17 #pragma comment(lib, "userenv.lib")
15 18
16 #include "base/basictypes.h" 19 #include "base/basictypes.h"
20 #include "base/file_util.h"
21 #include "base/files/file_path.h"
17 #include "base/json/json_reader.h" 22 #include "base/json/json_reader.h"
23 #include "base/lazy_instance.h"
18 #include "base/logging.h" 24 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h" 25 #include "base/scoped_native_library.h"
26 #include "base/stl_util.h"
20 #include "base/string16.h" 27 #include "base/string16.h"
28 #include "base/string_util.h"
21 #include "base/strings/string_number_conversions.h" 29 #include "base/strings/string_number_conversions.h"
30 #include "base/sys_byteorder.h"
22 #include "base/utf_string_conversions.h" 31 #include "base/utf_string_conversions.h"
23 #include "base/values.h"
24 #include "base/win/registry.h" 32 #include "base/win/registry.h"
25 #include "chrome/browser/policy/policy_bundle.h" 33 #include "chrome/browser/policy/policy_bundle.h"
26 #include "chrome/browser/policy/policy_map.h" 34 #include "chrome/browser/policy/policy_map.h"
35 #include "chrome/browser/policy/preg_parser_win.h"
27 #include "chrome/common/json_schema/json_schema_constants.h" 36 #include "chrome/common/json_schema/json_schema_constants.h"
28 #include "policy/policy_constants.h" 37 #include "policy/policy_constants.h"
29 38
30 namespace schema = json_schema_constants; 39 namespace schema = json_schema_constants;
31 40
32 using base::win::RegKey; 41 using base::win::RegKey;
33 using base::win::RegistryKeyIterator; 42 using base::win::RegistryKeyIterator;
34 using base::win::RegistryValueIterator; 43 using base::win::RegistryValueIterator;
35 using namespace policy::registry_constants;
36 44
37 namespace policy { 45 namespace policy {
38 46
39 namespace registry_constants {
40 const wchar_t kPathSep[] = L"\\";
41 const wchar_t kThirdParty[] = L"3rdparty";
42 const wchar_t kMandatory[] = L"policy";
43 const wchar_t kRecommended[] = L"recommended";
44 const wchar_t kSchema[] = L"schema";
45 } // namespace registry_constants
46
47 namespace { 47 namespace {
48 48
49 // Map of registry hives to their corresponding policy scope, in decreasing 49 const char kKeyMandatory[] = "policy";
50 // order of priority. 50 const char kKeyRecommended[] = "recommended";
51 const struct { 51 const char kKeySchema[] = "schema";
52 HKEY hive; 52 const char kKeyThirdParty[] = "3rdparty";
53 PolicyScope scope; 53
54 } kHives[] = { 54 // The GUID of the registry settings group policy extension.
55 { HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE }, 55 GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID;
56 { HKEY_CURRENT_USER, POLICY_SCOPE_USER }, 56
57 // The PReg file name.
58 const base::FilePath::CharType kPRegFileName[] =
59 FILE_PATH_LITERAL("Registry.pol");
60
61 // A helper class encapsulating run-time-linked function calls to Wow64 APIs.
62 class Wow64Functions {
63 public:
64 Wow64Functions()
65 : kernel32_lib_(base::FilePath(L"kernel32")),
66 is_wow_64_process_(NULL),
67 wow_64_disable_wow_64_fs_redirection_(NULL),
68 wow_64_revert_wow_64_fs_redirection_(NULL) {
69 if (kernel32_lib_.is_valid()) {
70 is_wow_64_process_ = static_cast<IsWow64Process>(
71 kernel32_lib_.GetFunctionPointer("IsWow64Process"));
72 wow_64_disable_wow_64_fs_redirection_ =
73 static_cast<Wow64DisableWow64FSRedirection>(
74 kernel32_lib_.GetFunctionPointer(
75 "Wow64DisableWow64FsRedirection"));
76 wow_64_revert_wow_64_fs_redirection_ =
77 static_cast<Wow64RevertWow64FSRedirection>(
78 kernel32_lib_.GetFunctionPointer(
79 "Wow64RevertWow64FsRedirection"));
80 }
81 }
82
83 bool is_valid() {
84 return is_wow_64_process_ &&
85 wow_64_disable_wow_64_fs_redirection_ &&
86 wow_64_revert_wow_64_fs_redirection_;
87 }
88
89 bool IsWow64() {
90 BOOL result = 0;
91 if (!is_wow_64_process_(GetCurrentProcess(), &result))
92 PLOG(WARNING) << "IsWow64ProcFailed";
93 return !!result;
94 }
95
96 bool DisableFsRedirection(PVOID* previous_state) {
97 return !!wow_64_disable_wow_64_fs_redirection_(previous_state);
98 }
99
100 bool RevertFsRedirection(PVOID previous_state) {
101 return !!wow_64_revert_wow_64_fs_redirection_(previous_state);
102 }
103
104 private:
105 base::ScopedNativeLibrary kernel32_lib_;
Joao da Silva 2013/04/10 12:32:12 nit: fields after types
Mattias Nissler (ping if slow) 2013/04/10 13:20:25 Done.
106
107 typedef BOOL (WINAPI* IsWow64Process)(HANDLE, PBOOL);
108 typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*);
109 typedef BOOL (WINAPI* Wow64RevertWow64FSRedirection)(PVOID);
110
111 IsWow64Process is_wow_64_process_;
112 Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_;
113 Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_;
114
115 DISALLOW_COPY_AND_ASSIGN(Wow64Functions);
57 }; 116 };
58 117
59 // Reads a REG_SZ string at |key| named |name| into |result|. Returns false if 118 // Global Wow64Function instance used by ScopedDisableWow64Redirection below.
60 // the string could not be read. 119 static base::LazyInstance<Wow64Functions> g_wow_64_functions =
61 bool ReadRegistryString(RegKey* key, 120 LAZY_INSTANCE_INITIALIZER;
62 const string16& name,
63 string16* result) {
64 DWORD value_size = 0;
65 DWORD key_type = 0;
66 scoped_array<uint8> buffer;
67 121
68 if (key->ReadValue(name.c_str(), 0, &value_size, &key_type) != ERROR_SUCCESS) 122 // Scoper that switches off Wow64 File System Redirection during its lifetime.
69 return false; 123 class ScopedDisableWow64Redirection {
70 if (key_type != REG_SZ) 124 public:
71 return false; 125 ScopedDisableWow64Redirection()
126 : active_(false),
127 previous_state_(NULL) {
128 Wow64Functions* wow64 = g_wow_64_functions.Pointer();
129 if (wow64->is_valid() && wow64->IsWow64()) {
130 if (wow64->DisableFsRedirection(&previous_state_))
131 active_ = true;
132 else
133 PLOG(WARNING) << "Wow64DisableWow64FSRedirection";
134 }
135 }
72 136
73 // According to the Microsoft documentation, the string 137 ~ScopedDisableWow64Redirection() {
74 // buffer may not be explicitly 0-terminated. Allocate a 138 if (active_)
75 // slightly larger buffer and pre-fill to zeros to guarantee 139 CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_));
76 // the 0-termination. 140 }
77 buffer.reset(new uint8[value_size + 2]); 141
78 memset(buffer.get(), 0, value_size + 2); 142 bool is_active() { return active_; }
79 key->ReadValue(name.c_str(), buffer.get(), &value_size, NULL); 143
80 result->assign(reinterpret_cast<const wchar_t*>(buffer.get())); 144 private:
81 return true; 145 bool active_;
146 PVOID previous_state_;
147
148 DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection);
149 };
150
151 // AppliedGPOListProvider implementation that calls actual Windows APIs.
152 class WinGPOListProvider : public AppliedGPOListProvider {
153 public:
154 virtual ~WinGPOListProvider() {}
155
156 // AppliedGPOListProvider:
157 virtual DWORD GetAppliedGPOList(DWORD flags,
158 LPCTSTR machine_name,
159 PSID sid_user,
160 GUID* extension_guid,
161 PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE{
Joao da Silva 2013/04/10 12:32:12 nit: space between OVERRIDE and {
Mattias Nissler (ping if slow) 2013/04/10 13:20:25 Done.
162 return ::GetAppliedGPOList(flags, machine_name, sid_user, extension_guid,
163 gpo_list);
164 }
165
166 virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE {
167 return ::FreeGPOList(gpo_list);
168 }
169 };
170
171 // The default windows GPO list provider used for PolicyLoaderWin.
172 static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider =
173 LAZY_INSTANCE_INITIALIZER;
174
175 // Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
176 const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
177 const std::string& name) {
178 if (!dictionary)
179 return NULL;
180 const base::DictionaryValue* entry = NULL;
181 dictionary->GetDictionaryWithoutPathExpansion(name, &entry);
182 return entry;
82 } 183 }
83 184
84 // Reads a REG_DWORD integer at |key| named |name| into |result|. Returns false 185 // Tries to extract the dictionary at |key| in |dict| and returns it.
85 // if the value could no be read. 186 scoped_ptr<base::DictionaryValue> RemoveDict(base::DictionaryValue* dict,
86 bool ReadRegistryInteger(RegKey* key, 187 const std::string& key) {
87 const string16& name, 188 base::Value* entry = NULL;
88 uint32* result) { 189 base::DictionaryValue* result_dict = NULL;
89 DWORD dword; 190 if (dict && dict->RemoveWithoutPathExpansion(key, &entry) && entry) {
90 if (key->ReadValueDW(name.c_str(), &dword) != ERROR_SUCCESS) 191 if (!entry->GetAsDictionary(&result_dict))
91 return false; 192 delete entry;
92 *result = dword; 193 }
93 return true; 194
195 return make_scoped_ptr(result_dict);
196 }
197
198 std::string GetSchemaTypeForValueType(base::Value::Type value_type) {
199 switch (value_type) {
200 case base::Value::TYPE_DICTIONARY:
201 return json_schema_constants::kObject;
202 case base::Value::TYPE_INTEGER:
203 return json_schema_constants::kInteger;
204 case base::Value::TYPE_LIST:
205 return json_schema_constants::kArray;
206 case base::Value::TYPE_BOOLEAN:
207 return json_schema_constants::kBoolean;
208 case base::Value::TYPE_STRING:
209 return json_schema_constants::kString;
210 default:
211 break;
212 }
213
214 NOTREACHED() << "Unsupported policy value type " << value_type;
215 return json_schema_constants::kNull;
94 } 216 }
95 217
96 // Returns the Value type described in |schema|, or |default_type| if not found. 218 // Returns the Value type described in |schema|, or |default_type| if not found.
97 base::Value::Type GetType(const base::DictionaryValue* schema, 219 base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema,
98 base::Value::Type default_type) { 220 base::Value::Type default_type) {
99 // JSON-schema types to base::Value::Type mapping. 221 // JSON-schema types to base::Value::Type mapping.
100 static const struct { 222 static const struct {
101 // JSON schema type. 223 // JSON schema type.
102 const char* schema_type; 224 const char* schema_type;
103 // Correspondent value type. 225 // Correspondent value type.
104 base::Value::Type value_type; 226 base::Value::Type value_type;
105 } kSchemaToValueTypeMap[] = { 227 } kSchemaToValueTypeMap[] = {
106 { schema::kArray, base::Value::TYPE_LIST }, 228 { schema::kArray, base::Value::TYPE_LIST },
107 { schema::kBoolean, base::Value::TYPE_BOOLEAN }, 229 { schema::kBoolean, base::Value::TYPE_BOOLEAN },
108 { schema::kInteger, base::Value::TYPE_INTEGER }, 230 { schema::kInteger, base::Value::TYPE_INTEGER },
109 { schema::kNull, base::Value::TYPE_NULL }, 231 { schema::kNull, base::Value::TYPE_NULL },
110 { schema::kNumber, base::Value::TYPE_DOUBLE }, 232 { schema::kNumber, base::Value::TYPE_DOUBLE },
111 { schema::kObject, base::Value::TYPE_DICTIONARY }, 233 { schema::kObject, base::Value::TYPE_DICTIONARY },
112 { schema::kString, base::Value::TYPE_STRING }, 234 { schema::kString, base::Value::TYPE_STRING },
113 }; 235 };
114 236
115 if (!schema) 237 if (!schema)
116 return default_type; 238 return default_type;
117 std::string type; 239 std::string type;
118 if (!schema->GetString(schema::kType, &type)) 240 if (!schema->GetStringWithoutPathExpansion(schema::kType, &type))
119 return default_type; 241 return default_type;
120 for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) { 242 for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
121 if (type == kSchemaToValueTypeMap[i].schema_type) 243 if (type == kSchemaToValueTypeMap[i].schema_type)
122 return kSchemaToValueTypeMap[i].value_type; 244 return kSchemaToValueTypeMap[i].value_type;
123 } 245 }
124 return default_type; 246 return default_type;
125 } 247 }
126 248
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. 249 // Returns the schema for property |name| given the |schema| of an object.
145 // Returns the "additionalProperties" schema if no specific schema for 250 // Returns the "additionalProperties" schema if no specific schema for
146 // |name| is present. Returns NULL if no schema is found. 251 // |name| is present. Returns NULL if no schema is found.
147 const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema, 252 const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
148 const std::string& name) { 253 const std::string& name) {
149 const base::DictionaryValue* properties = 254 const base::DictionaryValue* properties =
150 GetEntry(schema, schema::kProperties); 255 GetEntry(schema, schema::kProperties);
151 const base::DictionaryValue* sub_schema = GetEntry(properties, name); 256 const base::DictionaryValue* sub_schema = GetEntry(properties, name);
152 if (sub_schema) 257 if (sub_schema)
153 return sub_schema; 258 return sub_schema;
154 // "additionalProperties" can be a boolean, but that case is ignored. 259 // "additionalProperties" can be a boolean, but that case is ignored.
155 return GetEntry(schema, schema::kAdditionalProperties); 260 return GetEntry(schema, schema::kAdditionalProperties);
156 } 261 }
157 262
158 // Converts string |value| to another |type|, if possible. 263 // Reads the subtree of the Windows registry at |root| into the passed |dict|.
159 base::Value* ConvertStringValue(const string16& value, base::Value::Type type) { 264 void ReadRegistry(HKEY hive,
160 switch (type) { 265 const string16& root,
161 case base::Value::TYPE_NULL: 266 base::DictionaryValue* dict) {
162 return base::Value::CreateNullValue(); 267 // First, read all the values of the key.
163 268 for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
269 const std::string name = UTF16ToUTF8(it.Name());
270 switch (it.Type()) {
271 case REG_SZ:
272 case REG_EXPAND_SZ:
273 dict->SetStringWithoutPathExpansion(name, UTF16ToUTF8(it.Value()));
274 continue;
275 case REG_DWORD_LITTLE_ENDIAN:
276 case REG_DWORD_BIG_ENDIAN:
277 if (it.ValueSize() == sizeof(DWORD)) {
278 DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
279 if (it.Type() == REG_DWORD_BIG_ENDIAN)
280 dword_value = base::NetToHost32(dword_value);
281 else
282 dword_value = base::ByteSwapToLE32(dword_value);
283 dict->SetIntegerWithoutPathExpansion(name, dword_value);
284 continue;
285 }
286 case REG_NONE:
287 case REG_LINK:
288 case REG_MULTI_SZ:
289 case REG_RESOURCE_LIST:
290 case REG_FULL_RESOURCE_DESCRIPTOR:
291 case REG_RESOURCE_REQUIREMENTS_LIST:
292 case REG_QWORD_LITTLE_ENDIAN:
293 // Unsupported type, message gets logged below.
294 break;
295 }
296
297 LOG(WARNING) << "Failed to read hive " << hive << " at "
298 << root << "\\" << name
299 << " type " << it.Type();
300 }
301
302 // Recurse for all subkeys.
303 for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
304 std::string name(UTF16ToUTF8(it.Name()));
305 if (dict->HasKey(name)) {
306 DLOG(WARNING) << "Ignoring registry key because a value exists with the "
307 "same name: " << root << "\\" << name;
308 } else {
309 scoped_ptr<base::DictionaryValue> subdict(new base::DictionaryValue());
310 ReadRegistry(hive, root + L"\\" + it.Name(), subdict.get());
311 dict->SetWithoutPathExpansion(name, subdict.release());
312 }
313 }
314 }
315
316 // Converts |value| in raw GPO representation to the internal policy value, as
317 // described by |schema|. This maps the ambiguous GPO data types to the
318 // internal policy value representations.
319 scoped_ptr<base::Value> ConvertPolicyValue(
320 const base::Value& value,
321 const base::DictionaryValue* schema) {
322 // Figure out the type to convert to from the schema.
323 const base::Value::Type result_type(
324 GetValueTypeForSchema(schema, value.GetType()));
325
326 // If the type is good already, go with it.
327 if (value.IsType(result_type)) {
328 // Recurse for complex types if there is a schema.
329 if (schema) {
330 const base::DictionaryValue* dict = NULL;
331 const base::ListValue* list = NULL;
332 if (value.GetAsDictionary(&dict)) {
333 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
334 for (base::DictionaryValue::Iterator entry(*dict); entry.HasNext();
335 entry.Advance()) {
336 scoped_ptr<base::Value> converted_value(
337 ConvertPolicyValue(entry.value(),
338 GetSchemaFor(schema, entry.key())));
339 result->SetWithoutPathExpansion(entry.key(),
340 converted_value.release());
341 }
342 return result.Pass();
343 } else if (value.GetAsList(&list)) {
344 scoped_ptr<base::ListValue> result(new base::ListValue());
345 const base::DictionaryValue* item_schema =
346 GetEntry(schema, schema::kItems);
347 for (base::ListValue::const_iterator entry(list->begin());
348 entry != list->end(); ++entry) {
349 result->Append(ConvertPolicyValue(**entry, item_schema).release());
350 }
351 return result.Pass();
352 }
353 }
354 return make_scoped_ptr(value.DeepCopy());
355 }
356
357 // Else, do some conversions to map windows registry data types to JSON types.
358 std::string string_value;
359 int int_value = 0;
360 switch (result_type) {
361 case base::Value::TYPE_NULL: {
362 return make_scoped_ptr(base::Value::CreateNullValue());
363 }
164 case base::Value::TYPE_BOOLEAN: { 364 case base::Value::TYPE_BOOLEAN: {
165 int int_value; 365 // Accept booleans encoded as either string or integer.
166 if (base::StringToInt(value, &int_value)) 366 if (value.GetAsInteger(&int_value) ||
167 return base::Value::CreateBooleanValue(int_value != 0); 367 (value.GetAsString(&string_value) &&
168 return NULL; 368 base::StringToInt(string_value, &int_value))) {
169 } 369 return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0));
170 370 }
371 break;
372 }
171 case base::Value::TYPE_INTEGER: { 373 case base::Value::TYPE_INTEGER: {
172 int int_value; 374 // Integers may be string-encoded.
173 if (base::StringToInt(value, &int_value)) 375 if (value.GetAsString(&string_value) &&
174 return base::Value::CreateIntegerValue(int_value); 376 base::StringToInt(string_value, &int_value)) {
175 return NULL; 377 return make_scoped_ptr(base::Value::CreateIntegerValue(int_value));
176 } 378 }
177 379 break;
380 }
178 case base::Value::TYPE_DOUBLE: { 381 case base::Value::TYPE_DOUBLE: {
179 double double_value; 382 // Doubles may be string-encoded or integer-encoded.
180 if (base::StringToDouble(UTF16ToUTF8(value), &double_value)) 383 double double_value = 0;
181 return base::Value::CreateDoubleValue(double_value); 384 if (value.GetAsInteger(&int_value)) {
182 DLOG(WARNING) << "Failed to read policy value as double: " << value; 385 return make_scoped_ptr(base::Value::CreateDoubleValue(int_value));
183 return NULL; 386 } else if (value.GetAsString(&string_value) &&
184 } 387 base::StringToDouble(string_value, &double_value)) {
185 388 return make_scoped_ptr(base::Value::CreateDoubleValue(double_value));
186 case base::Value::TYPE_STRING: 389 }
187 return base::Value::CreateStringValue(value); 390 break;
188 391 }
189 case base::Value::TYPE_DICTIONARY: 392 case base::Value::TYPE_LIST: {
190 case base::Value::TYPE_LIST: 393 // Lists are encoded as subkeys with numbered value in the registry.
191 return base::JSONReader::Read(UTF16ToUTF8(value)); 394 const base::DictionaryValue* dict = NULL;
192 395 if (value.GetAsDictionary(&dict)) {
193 case base::Value::TYPE_BINARY: 396 scoped_ptr<base::ListValue> result(new base::ListValue());
194 DLOG(WARNING) << "Cannot convert REG_SZ entry to type " << type; 397 const base::DictionaryValue* item_schema =
195 return NULL; 398 GetEntry(schema, schema::kItems);
196 } 399 for (int i = 1; ; ++i) {
197 NOTREACHED(); 400 const base::Value* entry = NULL;
198 return NULL; 401 if (!dict->Get(base::IntToString(i), &entry))
199 } 402 break;
200 403 result->Append(ConvertPolicyValue(*entry, item_schema).release());
201 // Converts an integer |value| to another |type|, if possible. 404 }
202 base::Value* ConvertIntegerValue(uint32 value, base::Value::Type type) { 405 return result.Pass();
203 switch (type) { 406 }
204 case base::Value::TYPE_BOOLEAN: 407 // Fall through in order to accept lists encoded as JSON strings.
205 return base::Value::CreateBooleanValue(value != 0); 408 }
206 409 case base::Value::TYPE_DICTIONARY: {
207 case base::Value::TYPE_INTEGER: 410 // Dictionaries may be encoded as JSON strings.
208 return base::Value::CreateIntegerValue(value); 411 if (value.GetAsString(&string_value)) {
209 412 scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
210 case base::Value::TYPE_DOUBLE: 413 if (result && result->IsType(result_type))
211 return base::Value::CreateDoubleValue(value); 414 return result.Pass();
212 415 }
213 case base::Value::TYPE_NULL: 416 break;
417 }
214 case base::Value::TYPE_STRING: 418 case base::Value::TYPE_STRING:
215 case base::Value::TYPE_BINARY: 419 case base::Value::TYPE_BINARY:
216 case base::Value::TYPE_DICTIONARY: 420 // No conversion possible.
217 case base::Value::TYPE_LIST: 421 break;
218 DLOG(WARNING) << "Cannot convert REG_DWORD entry to type " << type; 422 }
219 return NULL; 423
220 } 424 LOG(WARNING) << "Failed to convert " << value.GetType()
221 NOTREACHED(); 425 << " to " << result_type;
222 return NULL; 426 return make_scoped_ptr(base::Value::CreateNullValue());
223 } 427 }
224 428
225 // Reads a value from the registry |key| named |name| with registry type 429 // Parses |gpo_dict| according to |schema| and writes the resulting policy
226 // |registry_type| as a value of type |type|. 430 // settings to |policy| for the given |scope| and |level|.
227 // Returns NULL if the value could not be loaded or converted. 431 void ParsePolicy(const base::DictionaryValue* gpo_dict,
228 base::Value* ReadPolicyValue(RegKey* key, 432 PolicyLevel level,
229 const string16& name, 433 PolicyScope scope,
230 DWORD registry_type, 434 const base::DictionaryValue* schema,
231 base::Value::Type type) { 435 PolicyMap* policy) {
232 switch (registry_type) { 436 if (!gpo_dict)
233 case REG_SZ: { 437 return;
234 string16 value; 438
235 if (ReadRegistryString(key, name, &value)) 439 scoped_ptr<base::Value> policy_value(ConvertPolicyValue(*gpo_dict, schema));
236 return ConvertStringValue(value, type); 440 const base::DictionaryValue* policy_dict = NULL;
237 break; 441 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) {
238 } 442 LOG(WARNING) << "Root policy object is not a dictionary!";
239 443 return;
240 case REG_DWORD: { 444 }
241 uint32 value; 445
242 if (ReadRegistryInteger(key, name, &value)) 446 policy->LoadFrom(policy_dict, level, scope);
243 return ConvertIntegerValue(value, type);
244 break;
245 }
246
247 default:
248 DLOG(WARNING) << "Registry type not supported for key " << name;
249 break;
250 }
251 return NULL;
252 }
253
254 // Forward declaration for ReadComponentListValue().
255 base::DictionaryValue* ReadComponentDictionaryValue(
256 HKEY hive,
257 const string16& path,
258 const base::DictionaryValue* schema);
259
260 // Loads the list at |path| in the given |hive|. |schema| is a JSON schema
261 // (http://json-schema.org/) that describes the expected type of the list.
262 // Ownership of the result is transferred to the caller.
263 base::ListValue* ReadComponentListValue(HKEY hive,
264 const string16& path,
265 const base::DictionaryValue* schema) {
266 // The sub-elements are indexed from 1 to N. They can be represented as
267 // registry values or registry keys though; use |schema| first to try to
268 // determine the right type, and if that fails default to STRING.
269
270 RegKey key(hive, path.c_str(), KEY_READ);
271 if (!key.Valid())
272 return NULL;
273
274 // Get the schema for list items.
275 schema = GetEntry(schema, schema::kItems);
276 base::Value::Type type = GetType(schema, base::Value::TYPE_STRING);
277 base::ListValue* list = new base::ListValue();
278 for (int i = 1; ; ++i) {
279 string16 name = base::IntToString16(i);
280 base::Value* value = NULL;
281 if (type == base::Value::TYPE_DICTIONARY) {
282 value =
283 ReadComponentDictionaryValue(hive, path + kPathSep + name, schema);
284 } else if (type == base::Value::TYPE_LIST) {
285 value = ReadComponentListValue(hive, path + kPathSep + name, schema);
286 } else {
287 DWORD reg_type;
288 key.ReadValue(name.c_str(), NULL, NULL, &reg_type);
289 if (reg_type != REG_NONE)
290 value = ReadPolicyValue(&key, name, reg_type, type);
291 }
292 if (value)
293 list->Append(value);
294 else
295 break;
296 }
297 return list;
298 }
299
300 // Loads the dictionary at |path| in the given |hive|. |schema| is a JSON
301 // schema (http://json-schema.org/) that describes the expected types for the
302 // dictionary entries. When the type for a certain entry isn't described in the
303 // schema, a default conversion takes place. |schema| can be NULL.
304 // Ownership of the result is transferred to the caller.
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 } 447 }
387 448
388 } // namespace 449 } // namespace
389 450
390 PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list) 451 PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list,
452 const string16& chrome_policy_key,
453 AppliedGPOListProvider* gpo_provider)
391 : is_initialized_(false), 454 : is_initialized_(false),
455 chrome_policy_key_(chrome_policy_key),
392 policy_list_(policy_list), 456 policy_list_(policy_list),
Joao da Silva 2013/04/10 12:32:12 The order in the header is different.
Mattias Nissler (ping if slow) 2013/04/10 13:20:25 Done.
457 gpo_provider_(gpo_provider),
393 user_policy_changed_event_(false, false), 458 user_policy_changed_event_(false, false),
394 machine_policy_changed_event_(false, false), 459 machine_policy_changed_event_(false, false),
395 user_policy_watcher_failed_(false), 460 user_policy_watcher_failed_(false),
396 machine_policy_watcher_failed_(false) { 461 machine_policy_watcher_failed_(false) {
397 if (!RegisterGPNotification(user_policy_changed_event_.handle(), false)) { 462 if (!RegisterGPNotification(user_policy_changed_event_.handle(), false)) {
398 DPLOG(WARNING) << "Failed to register user group policy notification"; 463 DPLOG(WARNING) << "Failed to register user group policy notification";
399 user_policy_watcher_failed_ = true; 464 user_policy_watcher_failed_ = true;
400 } 465 }
401 if (!RegisterGPNotification(machine_policy_changed_event_.handle(), true)) { 466 if (!RegisterGPNotification(machine_policy_changed_event_.handle(), true)) {
402 DPLOG(WARNING) << "Failed to register machine group policy notification."; 467 DPLOG(WARNING) << "Failed to register machine group policy notification.";
403 machine_policy_watcher_failed_ = true; 468 machine_policy_watcher_failed_ = true;
404 } 469 }
405 } 470 }
406 471
407 PolicyLoaderWin::~PolicyLoaderWin() { 472 PolicyLoaderWin::~PolicyLoaderWin() {
408 user_policy_watcher_.StopWatching(); 473 user_policy_watcher_.StopWatching();
409 machine_policy_watcher_.StopWatching(); 474 machine_policy_watcher_.StopWatching();
410 } 475 }
411 476
477 // static
478 scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create(
479 const PolicyDefinitionList* policy_list) {
480 return make_scoped_ptr(
481 new PolicyLoaderWin(policy_list, kRegistryChromePolicyKey,
482 g_win_gpo_list_provider.Pointer()));
483 }
484
412 void PolicyLoaderWin::InitOnFile() { 485 void PolicyLoaderWin::InitOnFile() {
413 is_initialized_ = true; 486 is_initialized_ = true;
414 SetupWatches(); 487 SetupWatches();
415 } 488 }
416 489
417 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { 490 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 491 // Reset the watches BEFORE reading the individual policies to avoid
427 // missing a change notification. 492 // missing a change notification.
428 if (is_initialized_) 493 if (is_initialized_)
429 SetupWatches(); 494 SetupWatches();
430 495
431 // |kKeyPaths| is in decreasing order of priority. 496 if (chrome_policy_schema_.empty())
497 BuildChromePolicySchema();
498
499 // Policy scope and corresponding hive.
432 static const struct { 500 static const struct {
433 const wchar_t* path; 501 PolicyScope scope;
434 PolicyLevel level; 502 HKEY hive;
435 } kKeyPaths[] = { 503 } kScopes[] = {
436 { kRegistryMandatorySubKey, POLICY_LEVEL_MANDATORY }, 504 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE },
437 { kRegistryRecommendedSubKey, POLICY_LEVEL_RECOMMENDED }, 505 { POLICY_SCOPE_USER, HKEY_CURRENT_USER },
438 }; 506 };
439 507
440 // Lookup at the mandatory path for both user and machine policies first, and 508 // Load policy data for the different scopes/levels and merge them.
441 // then at the recommended path. 509 scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
442 for (size_t k = 0; k < arraysize(kKeyPaths); ++k) { 510 PolicyMap* chrome_policy =
443 for (size_t h = 0; h < arraysize(kHives); ++h) { 511 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
444 // Iterate over keys and values at this hive and path. 512 for (size_t i = 0; i < arraysize(kScopes); ++i) {
445 HKEY hive = kHives[h].hive; 513 PolicyScope scope = kScopes[i].scope;
446 string16 path(kKeyPaths[k].path); 514 base::DictionaryValue gpo_dict;
447 RegKey key; 515
448 if (key.Open(hive, path.c_str(), KEY_READ) != ERROR_SUCCESS || 516 HANDLE policy_lock =
449 !key.Valid()) { 517 EnterCriticalPolicySection(scope == POLICY_SCOPE_MACHINE);
Joao da Silva 2013/04/10 12:32:12 Is this needed to read from the registry? If not t
Mattias Nissler (ping if slow) 2013/04/10 13:20:25 This makes sure GPO doesn't update behind our back
450 continue; 518 if (policy_lock == NULL)
451 } 519 PLOG(ERROR) << "EnterCriticalPolicySection";
452 520
453 // Iterate over values for most policies. 521 if (!ReadPolicyFromGPO(scope, &gpo_dict)) {
454 for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) { 522 VLOG(1) << "Failed to read GPO files for " << scope
455 std::string name(UTF16ToUTF8(it.Name())); 523 << " falling back to registry.";
456 // Skip if a higher-priority policy value was already inserted, or 524 ReadRegistry(kScopes[i].hive, chrome_policy_key_, &gpo_dict);
457 // if this is the default value (empty string). 525 }
458 if (chrome_policies->Get(name) || name.empty()) 526
459 continue; 527 if (!LeaveCriticalPolicySection(policy_lock))
Joao da Silva 2013/04/10 12:32:12 Is this safe if Enter failed?
Mattias Nissler (ping if slow) 2013/04/10 13:20:25 Yes and no :) Yes, because there's no reason why C
460 // Get the expected policy type, if this is a known policy. 528 PLOG(ERROR) << "LeaveCriticalPolicySection";
461 base::Value::Type type = GetDefaultFor(it.Type()); 529
462 for (const PolicyDefinitionList::Entry* e = policy_list_->begin; 530 // Remove special-cased entries from the GPO dictionary.
463 e != policy_list_->end; ++e) { 531 base::DictionaryValue* temp_dict = NULL;
464 if (name == e->name) { 532 scoped_ptr<base::DictionaryValue> recommended_dict(
465 type = e->value_type; 533 RemoveDict(&gpo_dict, kKeyRecommended));
466 break; 534 scoped_ptr<base::DictionaryValue> third_party_dict(
467 } 535 RemoveDict(&gpo_dict, kKeyThirdParty));
468 } 536
469 base::Value* value = ReadPolicyValue(&key, it.Name(), it.Type(), type); 537 // Load Chrome policy.
470 if (!value) 538 LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy);
471 value = base::Value::CreateNullValue(); 539 LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope,
472 chrome_policies->Set(name, kKeyPaths[k].level, kHives[h].scope, value); 540 chrome_policy);
473 } 541
474 542 // Load 3rd-party policy.
475 // Iterate over keys for policies of type string-list. 543 if (third_party_dict)
476 for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) { 544 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get());
477 std::string name(UTF16ToUTF8(it.Name())); 545 }
478 // Skip if a higher-priority policy value was already inserted, or 546
479 // if this is the 3rd party policy subkey. 547 return bundle.Pass();
480 const string16 kThirdParty16(kThirdParty); 548 }
481 if (chrome_policies->Get(name) || it.Name() == kThirdParty16) 549
482 continue; 550 void PolicyLoaderWin::BuildChromePolicySchema() {
483 string16 list_path = path + kPathSep + it.Name(); 551 scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue());
484 RegKey key; 552 for (const PolicyDefinitionList::Entry* e = policy_list_->begin;
485 if (key.Open(hive, list_path.c_str(), KEY_READ) != ERROR_SUCCESS || 553 e != policy_list_->end; ++e) {
486 !key.Valid()) { 554 const std::string schema_type = GetSchemaTypeForValueType(e->value_type);
487 continue; 555 scoped_ptr<base::DictionaryValue> entry_schema(new base::DictionaryValue());
488 } 556 entry_schema->SetStringWithoutPathExpansion(json_schema_constants::kType,
489 base::ListValue* result = new base::ListValue(); 557 schema_type);
490 string16 value; 558
491 int index = 0; 559 if (e->value_type == base::Value::TYPE_LIST) {
492 while (ReadRegistryString(&key, base::IntToString16(++index), &value)) 560 scoped_ptr<base::DictionaryValue> items_schema(
493 result->Append(base::Value::CreateStringValue(value)); 561 new base::DictionaryValue());
494 chrome_policies->Set(name, kKeyPaths[k].level, kHives[h].scope, result); 562 items_schema->SetStringWithoutPathExpansion(
495 } 563 json_schema_constants::kType, json_schema_constants::kString);
496 } 564 entry_schema->SetWithoutPathExpansion(json_schema_constants::kItems,
497 } 565 items_schema.release());
498 } 566 }
499 567 properties->SetWithoutPathExpansion(e->name, entry_schema.release());
500 void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) { 568 }
501 // Each 3rd party namespace can have policies on both HKLM and HKCU. They 569 chrome_policy_schema_.SetStringWithoutPathExpansion(
502 // should be merged, giving priority to HKLM for policies with the same name. 570 json_schema_constants::kType, json_schema_constants::kObject);
503 571 chrome_policy_schema_.SetWithoutPathExpansion(
504 // Map of known domain name to their enum values. 572 json_schema_constants::kProperties, properties.release());
573 }
574
575 bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file,
576 base::DictionaryValue* policy) {
577 // The following deals with the minor annoyance that Wow64 FS redirection
578 // might need to be turned off: This is the case if running as a 32-bit
579 // process on a 64-bit system, in which case Wow64 FS redirection redirects
580 // access to the %WINDIR%/System32/GroupPolicy directory to
581 // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the
582 // system-native directory.
583 if (file_util::PathExists(preg_file)) {
584 return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy);
585 } else {
586 // Try with redirection switched off.
587 ScopedDisableWow64Redirection redirection_disable;
588 if (redirection_disable.is_active() && file_util::PathExists(preg_file))
589 return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy);
590 }
591
592 // Report the error.
593 LOG(ERROR) << "PReg file doesn't exist: " << preg_file.value();
594 return false;
595 }
596
597 bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope,
598 PGROUP_POLICY_OBJECT policy_object_list,
599 base::DictionaryValue* policy) {
600 base::DictionaryValue parsed_policy;
601 base::DictionaryValue forced_policy;
602 for (GROUP_POLICY_OBJECT* policy_object = policy_object_list;
603 policy_object; policy_object = policy_object->pNext) {
604 if (policy_object->dwOptions & GPO_FLAG_DISABLE)
605 continue;
606
607 if (PathIsUNC(policy_object->lpFileSysPath)) {
608 // UNC path: Assume this is an AD-managed machine, which updates the
609 // registry via GPO's standard registry CSE periodically. Fall back to
610 // reading from the registry in this case.
611 return false;
612 }
613
614 base::FilePath preg_file_path(
615 base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName));
616 if (policy_object->dwOptions & GPO_FLAG_FORCE) {
617 base::DictionaryValue new_forced_policy;
618 if (!ReadPRegFile(preg_file_path, &new_forced_policy))
619 return false;
620
621 // Merge with existing forced policy, giving precedence to the existing
622 // forced policy.
623 new_forced_policy.MergeDictionary(&forced_policy);
624 forced_policy.Swap(&new_forced_policy);
625 } else {
626 if (!ReadPRegFile(preg_file_path, &parsed_policy))
627 return false;
628 }
629 }
630
631 // Merge, give precedence to forced policy.
632 parsed_policy.MergeDictionary(&forced_policy);
633 policy->Swap(&parsed_policy);
634
635 return true;
636 }
637
638
639 bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope,
640 base::DictionaryValue* policy) {
641 PGROUP_POLICY_OBJECT policy_object_list = NULL;
642 DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0;
643 if (gpo_provider_->GetAppliedGPOList(
644 flags, NULL, NULL, &kRegistrySettingsCSEGUID,
645 &policy_object_list) != ERROR_SUCCESS) {
646 PLOG(ERROR) << "GetAppliedGPOList scope " << scope;
647 return false;
648 }
649
650 bool result = LoadGPOPolicy(scope, policy_object_list, policy);
651 if (!gpo_provider_->FreeGPOList(policy_object_list))
652 LOG(WARNING) << "FreeGPOList";
653
654 return result;
655 }
656
657 void PolicyLoaderWin::LoadChromePolicy(const base::DictionaryValue* gpo_dict,
658 PolicyLevel level,
659 PolicyScope scope,
660 PolicyMap* chrome_policy_map) {
661 PolicyMap policy;
662 ParsePolicy(gpo_dict, level, scope, &chrome_policy_schema_, &policy);
663 chrome_policy_map->MergeFrom(policy);
664 }
665
666 void PolicyLoaderWin::Load3rdPartyPolicy(
667 const DictionaryValue* gpo_dict,
668 PolicyScope scope,
669 PolicyBundle* bundle) {
670 // Map of known 3rd party policy domain name to their enum values.
505 static const struct { 671 static const struct {
506 const char* name; 672 const char* name;
507 PolicyDomain domain; 673 PolicyDomain domain;
508 } kDomains[] = { 674 } k3rdPartyDomains[] = {
509 { "extensions", POLICY_DOMAIN_EXTENSIONS }, 675 { "extensions", POLICY_DOMAIN_EXTENSIONS },
510 }; 676 };
511 677
512 // Map of policy paths to their corresponding policy level, in decreasing 678 // Policy level and corresponding path.
513 // order of priority.
514 static const struct { 679 static const struct {
680 PolicyLevel level;
515 const char* path; 681 const char* path;
516 PolicyLevel level; 682 } kLevels[] = {
517 } kKeyPaths[] = { 683 { POLICY_LEVEL_MANDATORY, kKeyMandatory },
518 { "policy", POLICY_LEVEL_MANDATORY }, 684 { POLICY_LEVEL_RECOMMENDED, kKeyRecommended },
519 { "recommended", POLICY_LEVEL_RECOMMENDED },
520 }; 685 };
521 686
522 // Path where policies for components are stored. 687 for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) {
523 const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep + 688 const char* name = k3rdPartyDomains[i].name;
524 kThirdParty + kPathSep; 689 const PolicyDomain domain = k3rdPartyDomains[i].domain;
525 690 const base::DictionaryValue* domain_dict = NULL;
526 for (size_t h = 0; h < arraysize(kHives); ++h) { 691 if (!gpo_dict->GetDictionaryWithoutPathExpansion(name, &domain_dict) ||
527 HKEY hkey = kHives[h].hive; 692 !domain_dict) {
528 693 continue;
529 for (size_t d = 0; d < arraysize(kDomains); ++d) { 694 }
530 // Each subkey under this domain is a component of that domain. 695
531 // |domain_path| == SOFTWARE\Policies\Chromium\3rdparty\<domain> 696 for (base::DictionaryValue::Iterator component(*domain_dict);
532 string16 domain_path = kPathPrefix + ASCIIToUTF16(kDomains[d].name); 697 component.HasNext(); component.Advance()) {
533 698 const base::DictionaryValue* component_dict = NULL;
534 for (RegistryKeyIterator domain_iterator(hkey, domain_path.c_str()); 699 if (!component.value().GetAsDictionary(&component_dict) ||
535 domain_iterator.Valid(); ++domain_iterator) { 700 !component_dict) {
536 string16 component(domain_iterator.Name()); 701 continue;
537 string16 component_path = domain_path + kPathSep + component; 702 }
538 703
539 // Load the schema for this component's policy, if present. 704 // Load the schema.
540 scoped_ptr<base::DictionaryValue> schema( 705 scoped_ptr<base::Value> schema;
541 ReadRegistrySchema(hkey, component_path, kSchema)); 706 const base::DictionaryValue* schema_dict = NULL;
542 707 std::string schema_json;
543 for (size_t k = 0; k < arraysize(kKeyPaths); ++k) { 708 if (component_dict->GetStringWithoutPathExpansion(kKeySchema,
544 string16 path = 709 &schema_json)) {
545 component_path + kPathSep + ASCIIToUTF16(kKeyPaths[k].path); 710 schema.reset(base::JSONReader::Read(schema_json));
546 711 if (!schema || !schema->GetAsDictionary(&schema_dict)) {
547 scoped_ptr<base::DictionaryValue> dictionary( 712 LOG(WARNING) << "Failed to parse 3rd-part policy schema for "
548 ReadComponentDictionaryValue(hkey, path, schema.get())); 713 << domain << "/" << component.key();
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 } 714 }
561 } 715 }
562 } 716
563 } 717 // Parse policy.
718 for (size_t j = 0; j < arraysize(kLevels); j++) {
719 const base::DictionaryValue* policy_dict = NULL;
720 if (!component_dict->GetDictionaryWithoutPathExpansion(
721 kLevels[j].path, &policy_dict) ||
722 !policy_dict) {
723 continue;
724 }
725
726 PolicyMap policy;
727 ParsePolicy(policy_dict, kLevels[j].level, scope, schema_dict, &policy);
728 PolicyNamespace policy_namespace(domain, component.key());
729 bundle->Get(policy_namespace).MergeFrom(policy);
730 }
731 }
732 }
564 } 733 }
565 734
566 void PolicyLoaderWin::SetupWatches() { 735 void PolicyLoaderWin::SetupWatches() {
567 DCHECK(is_initialized_); 736 DCHECK(is_initialized_);
568 if (!user_policy_watcher_failed_ && 737 if (!user_policy_watcher_failed_ &&
569 !user_policy_watcher_.GetWatchedObject() && 738 !user_policy_watcher_.GetWatchedObject() &&
570 !user_policy_watcher_.StartWatching( 739 !user_policy_watcher_.StartWatching(
571 user_policy_changed_event_.handle(), this)) { 740 user_policy_changed_event_.handle(), this)) {
572 DLOG(WARNING) << "Failed to start watch for user policy change event"; 741 DLOG(WARNING) << "Failed to start watch for user policy change event";
573 user_policy_watcher_failed_ = true; 742 user_policy_watcher_failed_ = true;
574 } 743 }
575 if (!machine_policy_watcher_failed_ && 744 if (!machine_policy_watcher_failed_ &&
576 !machine_policy_watcher_.GetWatchedObject() && 745 !machine_policy_watcher_.GetWatchedObject() &&
577 !machine_policy_watcher_.StartWatching( 746 !machine_policy_watcher_.StartWatching(
578 machine_policy_changed_event_.handle(), this)) { 747 machine_policy_changed_event_.handle(), this)) {
579 DLOG(WARNING) << "Failed to start watch for machine policy change event"; 748 DLOG(WARNING) << "Failed to start watch for machine policy change event";
580 machine_policy_watcher_failed_ = true; 749 machine_policy_watcher_failed_ = true;
581 } 750 }
582 } 751 }
583 752
584 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { 753 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) {
585 DCHECK(object == user_policy_changed_event_.handle() || 754 DCHECK(object == user_policy_changed_event_.handle() ||
586 object == machine_policy_changed_event_.handle()) 755 object == machine_policy_changed_event_.handle())
587 << "unexpected object signaled policy reload, obj = " 756 << "unexpected object signaled policy reload, obj = "
588 << std::showbase << std::hex << object; 757 << std::showbase << std::hex << object;
589 Reload(false); 758 Reload(false);
590 } 759 }
591 760
592 } // namespace policy 761 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698