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 "components/policy/core/common/policy_loader_win.h" | 5 #include "components/policy/core/common/policy_loader_win.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <rpc.h> // For struct GUID | 8 #include <rpc.h> // For struct GUID |
9 #include <shlwapi.h> // For PathIsUNC() | 9 #include <shlwapi.h> // For PathIsUNC() |
10 #include <userenv.h> // For GPO functions | 10 #include <userenv.h> // For GPO functions |
11 | 11 |
12 #include <string> | 12 #include <string> |
13 #include <vector> | 13 #include <vector> |
14 | 14 |
15 // shlwapi.dll is required for PathIsUNC(). | 15 // shlwapi.dll is required for PathIsUNC(). |
16 #pragma comment(lib, "shlwapi.lib") | 16 #pragma comment(lib, "shlwapi.lib") |
17 // userenv.dll is required for various GPO functions. | 17 // userenv.dll is required for various GPO functions. |
18 #pragma comment(lib, "userenv.lib") | 18 #pragma comment(lib, "userenv.lib") |
19 | 19 |
20 #include "base/basictypes.h" | 20 #include "base/basictypes.h" |
21 #include "base/file_util.h" | 21 #include "base/file_util.h" |
22 #include "base/json/json_reader.h" | 22 #include "base/json/json_reader.h" |
| 23 #include "base/json/json_writer.h" |
23 #include "base/lazy_instance.h" | 24 #include "base/lazy_instance.h" |
24 #include "base/logging.h" | 25 #include "base/logging.h" |
25 #include "base/scoped_native_library.h" | 26 #include "base/scoped_native_library.h" |
26 #include "base/sequenced_task_runner.h" | 27 #include "base/sequenced_task_runner.h" |
27 #include "base/stl_util.h" | 28 #include "base/stl_util.h" |
28 #include "base/strings/string16.h" | 29 #include "base/strings/string16.h" |
29 #include "base/strings/string_util.h" | 30 #include "base/strings/string_util.h" |
| 31 #include "base/values.h" |
30 #include "components/json_schema/json_schema_constants.h" | 32 #include "components/json_schema/json_schema_constants.h" |
31 #include "components/policy/core/common/policy_bundle.h" | 33 #include "components/policy/core/common/policy_bundle.h" |
32 #include "components/policy/core/common/policy_load_status.h" | 34 #include "components/policy/core/common/policy_load_status.h" |
33 #include "components/policy/core/common/policy_map.h" | 35 #include "components/policy/core/common/policy_map.h" |
34 #include "components/policy/core/common/policy_namespace.h" | 36 #include "components/policy/core/common/policy_namespace.h" |
35 #include "components/policy/core/common/preg_parser_win.h" | 37 #include "components/policy/core/common/preg_parser_win.h" |
36 #include "components/policy/core/common/registry_dict_win.h" | 38 #include "components/policy/core/common/registry_dict_win.h" |
37 #include "components/policy/core/common/schema.h" | 39 #include "components/policy/core/common/schema.h" |
38 | 40 |
39 namespace schema = json_schema_constants; | 41 namespace schema = json_schema_constants; |
40 | 42 |
41 namespace policy { | 43 namespace policy { |
42 | 44 |
43 namespace { | 45 namespace { |
44 | 46 |
45 const char kKeyMandatory[] = "policy"; | 47 const char kKeyMandatory[] = "policy"; |
46 const char kKeyRecommended[] = "recommended"; | 48 const char kKeyRecommended[] = "recommended"; |
47 const char kKeySchema[] = "schema"; | 49 const char kKeySchema[] = "schema"; |
48 const char kKeyThirdParty[] = "3rdparty"; | 50 const char kKeyThirdParty[] = "3rdparty"; |
49 | 51 |
| 52 // The Legacy Browser Support was the first user of the policy-for-extensions |
| 53 // API, and relied on behavior that will be phased out. If this extension is |
| 54 // present then its policies will be loaded in a special way. |
| 55 // TODO(joaodasilva): remove this for M35. http://crbug.com/325349 |
| 56 const char kLegacyBrowserSupportExtensionId[] = |
| 57 "heildphpnddilhkemkielfhnkaagiabh"; |
| 58 |
50 // The GUID of the registry settings group policy extension. | 59 // The GUID of the registry settings group policy extension. |
51 GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID; | 60 GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID; |
52 | 61 |
| 62 // If the LBS extension is found and contains a schema in the registry then this |
| 63 // function is used to patch it, and make it compliant. The fix is to |
| 64 // add an "items" attribute to lists that don't declare it. |
| 65 std::string PatchSchema(const std::string& schema) { |
| 66 base::JSONParserOptions options = base::JSON_PARSE_RFC; |
| 67 scoped_ptr<base::Value> json(base::JSONReader::Read(schema, options)); |
| 68 base::DictionaryValue* dict = NULL; |
| 69 base::DictionaryValue* properties = NULL; |
| 70 if (!json || |
| 71 !json->GetAsDictionary(&dict) || |
| 72 !dict->GetDictionary(schema::kProperties, &properties)) { |
| 73 return schema; |
| 74 } |
| 75 |
| 76 for (base::DictionaryValue::Iterator it(*properties); |
| 77 !it.IsAtEnd(); it.Advance()) { |
| 78 base::DictionaryValue* policy_schema = NULL; |
| 79 std::string type; |
| 80 if (properties->GetDictionary(it.key(), &policy_schema) && |
| 81 policy_schema->GetString(schema::kType, &type) && |
| 82 type == schema::kArray && |
| 83 !policy_schema->HasKey(schema::kItems)) { |
| 84 scoped_ptr<base::DictionaryValue> items(new base::DictionaryValue()); |
| 85 items->SetString(schema::kType, schema::kString); |
| 86 policy_schema->Set(schema::kItems, items.release()); |
| 87 } |
| 88 } |
| 89 |
| 90 std::string serialized; |
| 91 base::JSONWriter::Write(json.get(), &serialized); |
| 92 return serialized; |
| 93 } |
| 94 |
53 // A helper class encapsulating run-time-linked function calls to Wow64 APIs. | 95 // A helper class encapsulating run-time-linked function calls to Wow64 APIs. |
54 class Wow64Functions { | 96 class Wow64Functions { |
55 public: | 97 public: |
56 Wow64Functions() | 98 Wow64Functions() |
57 : kernel32_lib_(base::FilePath(L"kernel32")), | 99 : kernel32_lib_(base::FilePath(L"kernel32")), |
58 is_wow_64_process_(NULL), | 100 is_wow_64_process_(NULL), |
59 wow_64_disable_wow_64_fs_redirection_(NULL), | 101 wow_64_disable_wow_64_fs_redirection_(NULL), |
60 wow_64_revert_wow_64_fs_redirection_(NULL) { | 102 wow_64_revert_wow_64_fs_redirection_(NULL) { |
61 if (kernel32_lib_.is_valid()) { | 103 if (kernel32_lib_.is_valid()) { |
62 is_wow_64_process_ = reinterpret_cast<IsWow64Process>( | 104 is_wow_64_process_ = reinterpret_cast<IsWow64Process>( |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 | 199 |
158 virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE { | 200 virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE { |
159 return ::FreeGPOList(gpo_list); | 201 return ::FreeGPOList(gpo_list); |
160 } | 202 } |
161 }; | 203 }; |
162 | 204 |
163 // The default windows GPO list provider used for PolicyLoaderWin. | 205 // The default windows GPO list provider used for PolicyLoaderWin. |
164 static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider = | 206 static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider = |
165 LAZY_INSTANCE_INITIALIZER; | 207 LAZY_INSTANCE_INITIALIZER; |
166 | 208 |
167 std::string GetSchemaTypeForValueType(base::Value::Type value_type) { | |
168 switch (value_type) { | |
169 case base::Value::TYPE_DICTIONARY: | |
170 return json_schema_constants::kObject; | |
171 case base::Value::TYPE_INTEGER: | |
172 return json_schema_constants::kInteger; | |
173 case base::Value::TYPE_LIST: | |
174 return json_schema_constants::kArray; | |
175 case base::Value::TYPE_BOOLEAN: | |
176 return json_schema_constants::kBoolean; | |
177 case base::Value::TYPE_STRING: | |
178 return json_schema_constants::kString; | |
179 default: | |
180 break; | |
181 } | |
182 | |
183 NOTREACHED() << "Unsupported policy value type " << value_type; | |
184 return json_schema_constants::kNull; | |
185 } | |
186 | |
187 // Parses |gpo_dict| according to |schema| and writes the resulting policy | 209 // Parses |gpo_dict| according to |schema| and writes the resulting policy |
188 // settings to |policy| for the given |scope| and |level|. | 210 // settings to |policy| for the given |scope| and |level|. |
189 void ParsePolicy(const RegistryDict* gpo_dict, | 211 void ParsePolicy(const RegistryDict* gpo_dict, |
190 PolicyLevel level, | 212 PolicyLevel level, |
191 PolicyScope scope, | 213 PolicyScope scope, |
192 const base::DictionaryValue* schema, | 214 const Schema& schema, |
193 PolicyMap* policy) { | 215 PolicyMap* policy) { |
194 if (!gpo_dict) | 216 if (!gpo_dict) |
195 return; | 217 return; |
196 | 218 |
197 scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema)); | 219 scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema)); |
198 const base::DictionaryValue* policy_dict = NULL; | 220 const base::DictionaryValue* policy_dict = NULL; |
199 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { | 221 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { |
200 LOG(WARNING) << "Root policy object is not a dictionary!"; | 222 LOG(WARNING) << "Root policy object is not a dictionary!"; |
201 return; | 223 return; |
202 } | 224 } |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 is_initialized_ = true; | 278 is_initialized_ = true; |
257 SetupWatches(); | 279 SetupWatches(); |
258 } | 280 } |
259 | 281 |
260 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { | 282 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { |
261 // Reset the watches BEFORE reading the individual policies to avoid | 283 // Reset the watches BEFORE reading the individual policies to avoid |
262 // missing a change notification. | 284 // missing a change notification. |
263 if (is_initialized_) | 285 if (is_initialized_) |
264 SetupWatches(); | 286 SetupWatches(); |
265 | 287 |
266 if (chrome_policy_schema_.empty()) | |
267 BuildChromePolicySchema(); | |
268 | |
269 // Policy scope and corresponding hive. | 288 // Policy scope and corresponding hive. |
270 static const struct { | 289 static const struct { |
271 PolicyScope scope; | 290 PolicyScope scope; |
272 HKEY hive; | 291 HKEY hive; |
273 } kScopes[] = { | 292 } kScopes[] = { |
274 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, | 293 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, |
275 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, | 294 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, |
276 }; | 295 }; |
277 | 296 |
278 // Load policy data for the different scopes/levels and merge them. | 297 // Load policy data for the different scopes/levels and merge them. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 chrome_policy); | 338 chrome_policy); |
320 | 339 |
321 // Load 3rd-party policy. | 340 // Load 3rd-party policy. |
322 if (third_party_dict) | 341 if (third_party_dict) |
323 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); | 342 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); |
324 } | 343 } |
325 | 344 |
326 return bundle.Pass(); | 345 return bundle.Pass(); |
327 } | 346 } |
328 | 347 |
329 void PolicyLoaderWin::BuildChromePolicySchema() { | |
330 // TODO(joaodasilva): use the Schema directly instead of building this | |
331 // DictionaryValue. | |
332 scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue()); | |
333 const Schema* chrome_schema = | |
334 schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")); | |
335 for (Schema::Iterator it = chrome_schema->GetPropertiesIterator(); | |
336 !it.IsAtEnd(); it.Advance()) { | |
337 const std::string schema_type = | |
338 GetSchemaTypeForValueType(it.schema().type()); | |
339 scoped_ptr<base::DictionaryValue> entry_schema(new base::DictionaryValue()); | |
340 entry_schema->SetStringWithoutPathExpansion(json_schema_constants::kType, | |
341 schema_type); | |
342 | |
343 if (it.schema().type() == base::Value::TYPE_LIST) { | |
344 scoped_ptr<base::DictionaryValue> items_schema( | |
345 new base::DictionaryValue()); | |
346 items_schema->SetStringWithoutPathExpansion( | |
347 json_schema_constants::kType, json_schema_constants::kString); | |
348 entry_schema->SetWithoutPathExpansion(json_schema_constants::kItems, | |
349 items_schema.release()); | |
350 } | |
351 properties->SetWithoutPathExpansion(it.key(), entry_schema.release()); | |
352 } | |
353 chrome_policy_schema_.SetStringWithoutPathExpansion( | |
354 json_schema_constants::kType, json_schema_constants::kObject); | |
355 chrome_policy_schema_.SetWithoutPathExpansion( | |
356 json_schema_constants::kProperties, properties.release()); | |
357 } | |
358 | |
359 bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file, | 348 bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file, |
360 RegistryDict* policy, | 349 RegistryDict* policy, |
361 PolicyLoadStatusSample* status) { | 350 PolicyLoadStatusSample* status) { |
362 // The following deals with the minor annoyance that Wow64 FS redirection | 351 // The following deals with the minor annoyance that Wow64 FS redirection |
363 // might need to be turned off: This is the case if running as a 32-bit | 352 // might need to be turned off: This is the case if running as a 32-bit |
364 // process on a 64-bit system, in which case Wow64 FS redirection redirects | 353 // process on a 64-bit system, in which case Wow64 FS redirection redirects |
365 // access to the %WINDIR%/System32/GroupPolicy directory to | 354 // access to the %WINDIR%/System32/GroupPolicy directory to |
366 // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the | 355 // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the |
367 // system-native directory. | 356 // system-native directory. |
368 if (base::PathExists(preg_file)) { | 357 if (base::PathExists(preg_file)) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
449 } | 438 } |
450 | 439 |
451 return result; | 440 return result; |
452 } | 441 } |
453 | 442 |
454 void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict, | 443 void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict, |
455 PolicyLevel level, | 444 PolicyLevel level, |
456 PolicyScope scope, | 445 PolicyScope scope, |
457 PolicyMap* chrome_policy_map) { | 446 PolicyMap* chrome_policy_map) { |
458 PolicyMap policy; | 447 PolicyMap policy; |
459 ParsePolicy(gpo_dict, level, scope, &chrome_policy_schema_, &policy); | 448 const Schema* chrome_schema = |
| 449 schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")); |
| 450 ParsePolicy(gpo_dict, level, scope, *chrome_schema, &policy); |
460 chrome_policy_map->MergeFrom(policy); | 451 chrome_policy_map->MergeFrom(policy); |
461 } | 452 } |
462 | 453 |
463 void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict, | 454 void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict, |
464 PolicyScope scope, | 455 PolicyScope scope, |
465 PolicyBundle* bundle) { | 456 PolicyBundle* bundle) { |
466 // Map of known 3rd party policy domain name to their enum values. | 457 // Map of known 3rd party policy domain name to their enum values. |
467 static const struct { | 458 static const struct { |
468 const char* name; | 459 const char* name; |
469 PolicyDomain domain; | 460 PolicyDomain domain; |
(...skipping 14 matching lines...) Expand all Loading... |
484 const char* name = k3rdPartyDomains[i].name; | 475 const char* name = k3rdPartyDomains[i].name; |
485 const PolicyDomain domain = k3rdPartyDomains[i].domain; | 476 const PolicyDomain domain = k3rdPartyDomains[i].domain; |
486 const RegistryDict* domain_dict = gpo_dict->GetKey(name); | 477 const RegistryDict* domain_dict = gpo_dict->GetKey(name); |
487 if (!domain_dict) | 478 if (!domain_dict) |
488 continue; | 479 continue; |
489 | 480 |
490 for (RegistryDict::KeyMap::const_iterator component( | 481 for (RegistryDict::KeyMap::const_iterator component( |
491 domain_dict->keys().begin()); | 482 domain_dict->keys().begin()); |
492 component != domain_dict->keys().end(); | 483 component != domain_dict->keys().end(); |
493 ++component) { | 484 ++component) { |
494 // Load the schema. | 485 const PolicyNamespace policy_namespace(domain, component->first); |
495 const base::DictionaryValue* schema_dict = NULL; | 486 |
496 scoped_ptr<base::Value> schema; | 487 const Schema* schema_from_map = schema_map()->GetSchema(policy_namespace); |
497 std::string schema_json; | 488 if (!schema_from_map) { |
498 const base::Value* schema_value = component->second->GetValue(kKeySchema); | 489 // This extension isn't installed or doesn't support policies. |
499 if (schema_value && schema_value->GetAsString(&schema_json)) { | 490 continue; |
500 schema.reset(base::JSONReader::Read(schema_json)); | 491 } |
501 if (!schema || !schema->GetAsDictionary(&schema_dict)) { | 492 Schema schema = *schema_from_map; |
502 LOG(WARNING) << "Failed to parse 3rd-part policy schema for " | 493 |
503 << domain << "/" << component->first; | 494 if (!schema.valid() && |
| 495 policy_namespace.domain == POLICY_DOMAIN_EXTENSIONS && |
| 496 policy_namespace.component_id == kLegacyBrowserSupportExtensionId) { |
| 497 // TODO(joaodasilva): remove this special treatment for LBS by M35. |
| 498 std::string schema_json; |
| 499 const base::Value* value = component->second->GetValue(kKeySchema); |
| 500 if (value && value->GetAsString(&schema_json)) { |
| 501 std::string error; |
| 502 schema = Schema::Parse(PatchSchema(schema_json), &error); |
| 503 if (!schema.valid()) |
| 504 LOG(WARNING) << "Invalid schema in the registry for LBS: " << error; |
504 } | 505 } |
505 } | 506 } |
506 | 507 |
507 // Parse policy. | 508 // Parse policy. |
508 for (size_t j = 0; j < arraysize(kLevels); j++) { | 509 for (size_t j = 0; j < arraysize(kLevels); j++) { |
509 const RegistryDict* policy_dict = | 510 const RegistryDict* policy_dict = |
510 component->second->GetKey(kLevels[j].path); | 511 component->second->GetKey(kLevels[j].path); |
511 if (!policy_dict) | 512 if (!policy_dict) |
512 continue; | 513 continue; |
513 | 514 |
514 PolicyMap policy; | 515 PolicyMap policy; |
515 ParsePolicy(policy_dict, kLevels[j].level, scope, schema_dict, &policy); | 516 ParsePolicy(policy_dict, kLevels[j].level, scope, schema, &policy); |
516 PolicyNamespace policy_namespace(domain, component->first); | |
517 bundle->Get(policy_namespace).MergeFrom(policy); | 517 bundle->Get(policy_namespace).MergeFrom(policy); |
518 } | 518 } |
519 } | 519 } |
520 } | 520 } |
521 } | 521 } |
522 | 522 |
523 void PolicyLoaderWin::SetupWatches() { | 523 void PolicyLoaderWin::SetupWatches() { |
524 DCHECK(is_initialized_); | 524 DCHECK(is_initialized_); |
525 if (!user_policy_watcher_failed_ && | 525 if (!user_policy_watcher_failed_ && |
526 !user_policy_watcher_.GetWatchedObject() && | 526 !user_policy_watcher_.GetWatchedObject() && |
(...skipping 13 matching lines...) Expand all Loading... |
540 | 540 |
541 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { | 541 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { |
542 DCHECK(object == user_policy_changed_event_.handle() || | 542 DCHECK(object == user_policy_changed_event_.handle() || |
543 object == machine_policy_changed_event_.handle()) | 543 object == machine_policy_changed_event_.handle()) |
544 << "unexpected object signaled policy reload, obj = " | 544 << "unexpected object signaled policy reload, obj = " |
545 << std::showbase << std::hex << object; | 545 << std::showbase << std::hex << object; |
546 Reload(false); | 546 Reload(false); |
547 } | 547 } |
548 | 548 |
549 } // namespace policy | 549 } // namespace policy |
OLD | NEW |