| 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 |