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 #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, ®_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 |
OLD | NEW |