Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 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 <lm.h> // For limits. | 8 #include <lm.h> // For limits. |
| 9 #include <ntdsapi.h> // For Ds[Un]Bind | 9 #include <ntdsapi.h> // For Ds[Un]Bind |
| 10 #include <rpc.h> // For struct GUID | 10 #include <rpc.h> // For struct GUID |
| 11 #include <shlwapi.h> // For PathIsUNC() | 11 #include <shlwapi.h> // For PathIsUNC() |
| 12 #include <stddef.h> | 12 #include <stddef.h> |
| 13 #include <userenv.h> // For GPO functions | 13 #include <userenv.h> // For GPO functions |
| 14 | 14 |
| 15 #include <memory> | |
| 15 #include <string> | 16 #include <string> |
| 16 #include <vector> | 17 #include <vector> |
| 17 | 18 |
| 18 #include "base/bind.h" | 19 #include "base/bind.h" |
| 19 #include "base/files/file_util.h" | 20 #include "base/files/file_util.h" |
| 20 #include "base/json/json_reader.h" | 21 #include "base/json/json_reader.h" |
| 21 #include "base/json/json_writer.h" | 22 #include "base/json/json_writer.h" |
| 22 #include "base/lazy_instance.h" | 23 #include "base/lazy_instance.h" |
| 23 #include "base/logging.h" | 24 #include "base/logging.h" |
| 24 #include "base/macros.h" | 25 #include "base/macros.h" |
| 25 #include "base/memory/scoped_ptr.h" | 26 #include "base/memory/ptr_util.h" |
| 26 #include "base/metrics/histogram.h" | 27 #include "base/metrics/histogram.h" |
| 27 #include "base/metrics/sparse_histogram.h" | 28 #include "base/metrics/sparse_histogram.h" |
| 28 #include "base/scoped_native_library.h" | 29 #include "base/scoped_native_library.h" |
| 29 #include "base/sequenced_task_runner.h" | 30 #include "base/sequenced_task_runner.h" |
| 30 #include "base/stl_util.h" | 31 #include "base/stl_util.h" |
| 31 #include "base/strings/string16.h" | 32 #include "base/strings/string16.h" |
| 32 #include "base/strings/string_util.h" | 33 #include "base/strings/string_util.h" |
| 33 #include "base/values.h" | 34 #include "base/values.h" |
| 34 #include "base/win/win_util.h" | 35 #include "base/win/win_util.h" |
| 35 #include "base/win/windows_version.h" | 36 #include "base/win/windows_version.h" |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 DOMAIN_CHECK_ERROR_GET_JOIN_INFO = 0, | 98 DOMAIN_CHECK_ERROR_GET_JOIN_INFO = 0, |
| 98 DOMAIN_CHECK_ERROR_DS_BIND = 1, | 99 DOMAIN_CHECK_ERROR_DS_BIND = 1, |
| 99 DOMAIN_CHECK_ERROR_SIZE, // Not a DomainCheckError. Must be last. | 100 DOMAIN_CHECK_ERROR_SIZE, // Not a DomainCheckError. Must be last. |
| 100 }; | 101 }; |
| 101 | 102 |
| 102 // If the LBS extension is found and contains a schema in the registry then this | 103 // If the LBS extension is found and contains a schema in the registry then this |
| 103 // function is used to patch it, and make it compliant. The fix is to | 104 // function is used to patch it, and make it compliant. The fix is to |
| 104 // add an "items" attribute to lists that don't declare it. | 105 // add an "items" attribute to lists that don't declare it. |
| 105 std::string PatchSchema(const std::string& schema) { | 106 std::string PatchSchema(const std::string& schema) { |
| 106 base::JSONParserOptions options = base::JSON_PARSE_RFC; | 107 base::JSONParserOptions options = base::JSON_PARSE_RFC; |
| 107 scoped_ptr<base::Value> json = base::JSONReader::Read(schema, options); | 108 std::unique_ptr<base::Value> json = base::JSONReader::Read(schema, options); |
| 108 base::DictionaryValue* dict = NULL; | 109 base::DictionaryValue* dict = NULL; |
| 109 base::DictionaryValue* properties = NULL; | 110 base::DictionaryValue* properties = NULL; |
| 110 if (!json || | 111 if (!json || |
| 111 !json->GetAsDictionary(&dict) || | 112 !json->GetAsDictionary(&dict) || |
| 112 !dict->GetDictionary(schema::kProperties, &properties)) { | 113 !dict->GetDictionary(schema::kProperties, &properties)) { |
| 113 return schema; | 114 return schema; |
| 114 } | 115 } |
| 115 | 116 |
| 116 for (base::DictionaryValue::Iterator it(*properties); | 117 for (base::DictionaryValue::Iterator it(*properties); |
| 117 !it.IsAtEnd(); it.Advance()) { | 118 !it.IsAtEnd(); it.Advance()) { |
| 118 base::DictionaryValue* policy_schema = NULL; | 119 base::DictionaryValue* policy_schema = NULL; |
| 119 std::string type; | 120 std::string type; |
| 120 if (properties->GetDictionary(it.key(), &policy_schema) && | 121 if (properties->GetDictionary(it.key(), &policy_schema) && |
| 121 policy_schema->GetString(schema::kType, &type) && | 122 policy_schema->GetString(schema::kType, &type) && |
| 122 type == schema::kArray && | 123 type == schema::kArray && |
| 123 !policy_schema->HasKey(schema::kItems)) { | 124 !policy_schema->HasKey(schema::kItems)) { |
| 124 scoped_ptr<base::DictionaryValue> items(new base::DictionaryValue()); | 125 std::unique_ptr<base::DictionaryValue> items(new base::DictionaryValue()); |
| 125 items->SetString(schema::kType, schema::kString); | 126 items->SetString(schema::kType, schema::kString); |
| 126 policy_schema->Set(schema::kItems, items.release()); | 127 policy_schema->Set(schema::kItems, items.release()); |
| 127 } | 128 } |
| 128 } | 129 } |
| 129 | 130 |
| 130 std::string serialized; | 131 std::string serialized; |
| 131 base::JSONWriter::Write(*json, &serialized); | 132 base::JSONWriter::Write(*json, &serialized); |
| 132 return serialized; | 133 return serialized; |
| 133 } | 134 } |
| 134 | 135 |
| 135 // Verifies that untrusted policies contain only safe values. Modifies the | 136 // Verifies that untrusted policies contain only safe values. Modifies the |
| 136 // |policy| in place. | 137 // |policy| in place. |
| 137 void FilterUntrustedPolicy(PolicyMap* policy) { | 138 void FilterUntrustedPolicy(PolicyMap* policy) { |
| 138 if (base::win::IsEnrolledToDomain()) | 139 if (base::win::IsEnrolledToDomain()) |
| 139 return; | 140 return; |
| 140 | 141 |
| 141 int invalid_policies = 0; | 142 int invalid_policies = 0; |
| 142 const PolicyMap::Entry* map_entry = | 143 const PolicyMap::Entry* map_entry = |
| 143 policy->Get(key::kExtensionInstallForcelist); | 144 policy->Get(key::kExtensionInstallForcelist); |
| 144 if (map_entry && map_entry->value) { | 145 if (map_entry && map_entry->value) { |
| 145 const base::ListValue* policy_list_value = NULL; | 146 const base::ListValue* policy_list_value = NULL; |
| 146 if (!map_entry->value->GetAsList(&policy_list_value)) | 147 if (!map_entry->value->GetAsList(&policy_list_value)) |
| 147 return; | 148 return; |
| 148 | 149 |
| 149 scoped_ptr<base::ListValue> filtered_values(new base::ListValue); | 150 std::unique_ptr<base::ListValue> filtered_values(new base::ListValue); |
| 150 for (base::ListValue::const_iterator list_entry(policy_list_value->begin()); | 151 for (base::ListValue::const_iterator list_entry(policy_list_value->begin()); |
| 151 list_entry != policy_list_value->end(); ++list_entry) { | 152 list_entry != policy_list_value->end(); ++list_entry) { |
| 152 std::string entry; | 153 std::string entry; |
| 153 if (!(*list_entry)->GetAsString(&entry)) | 154 if (!(*list_entry)->GetAsString(&entry)) |
| 154 continue; | 155 continue; |
| 155 size_t pos = entry.find(';'); | 156 size_t pos = entry.find(';'); |
| 156 if (pos == std::string::npos) | 157 if (pos == std::string::npos) |
| 157 continue; | 158 continue; |
| 158 // Only allow custom update urls in enterprise environments. | 159 // Only allow custom update urls in enterprise environments. |
| 159 if (!base::LowerCaseEqualsASCII(entry.substr(pos), | 160 if (!base::LowerCaseEqualsASCII(entry.substr(pos), |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 // Parses |gpo_dict| according to |schema| and writes the resulting policy | 311 // Parses |gpo_dict| according to |schema| and writes the resulting policy |
| 311 // settings to |policy| for the given |scope| and |level|. | 312 // settings to |policy| for the given |scope| and |level|. |
| 312 void ParsePolicy(const RegistryDict* gpo_dict, | 313 void ParsePolicy(const RegistryDict* gpo_dict, |
| 313 PolicyLevel level, | 314 PolicyLevel level, |
| 314 PolicyScope scope, | 315 PolicyScope scope, |
| 315 const Schema& schema, | 316 const Schema& schema, |
| 316 PolicyMap* policy) { | 317 PolicyMap* policy) { |
| 317 if (!gpo_dict) | 318 if (!gpo_dict) |
| 318 return; | 319 return; |
| 319 | 320 |
| 320 scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema)); | 321 std::unique_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema)); |
| 321 const base::DictionaryValue* policy_dict = NULL; | 322 const base::DictionaryValue* policy_dict = NULL; |
| 322 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { | 323 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { |
| 323 LOG(WARNING) << "Root policy object is not a dictionary!"; | 324 LOG(WARNING) << "Root policy object is not a dictionary!"; |
| 324 return; | 325 return; |
| 325 } | 326 } |
| 326 | 327 |
| 327 policy->LoadFrom(policy_dict, level, scope, POLICY_SOURCE_PLATFORM); | 328 policy->LoadFrom(policy_dict, level, scope, POLICY_SOURCE_PLATFORM); |
| 328 } | 329 } |
| 329 | 330 |
| 330 // Collects stats about the enterprise environment that can be used to decide | 331 // Collects stats about the enterprise environment that can be used to decide |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 395 ::UnregisterGPNotification(user_policy_changed_event_.handle()); | 396 ::UnregisterGPNotification(user_policy_changed_event_.handle()); |
| 396 user_policy_watcher_.StopWatching(); | 397 user_policy_watcher_.StopWatching(); |
| 397 } | 398 } |
| 398 if (!machine_policy_watcher_failed_) { | 399 if (!machine_policy_watcher_failed_) { |
| 399 ::UnregisterGPNotification(machine_policy_changed_event_.handle()); | 400 ::UnregisterGPNotification(machine_policy_changed_event_.handle()); |
| 400 machine_policy_watcher_.StopWatching(); | 401 machine_policy_watcher_.StopWatching(); |
| 401 } | 402 } |
| 402 } | 403 } |
| 403 | 404 |
| 404 // static | 405 // static |
| 405 scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create( | 406 std::unique_ptr<PolicyLoaderWin> PolicyLoaderWin::Create( |
| 406 scoped_refptr<base::SequencedTaskRunner> task_runner, | 407 scoped_refptr<base::SequencedTaskRunner> task_runner, |
| 407 const base::string16& chrome_policy_key) { | 408 const base::string16& chrome_policy_key) { |
| 408 return make_scoped_ptr( | 409 return base::WrapUnique(new PolicyLoaderWin( |
|
Thiemo Nagel
2016/04/20 20:34:19
Optional nit: Preserving existing indentation woul
dcheng
2016/04/20 20:56:31
I'd prefer not to fight clang-format here. If we k
| |
| 409 new PolicyLoaderWin(task_runner, | 410 task_runner, chrome_policy_key, g_win_gpo_list_provider.Pointer())); |
| 410 chrome_policy_key, | |
| 411 g_win_gpo_list_provider.Pointer())); | |
| 412 } | 411 } |
| 413 | 412 |
| 414 void PolicyLoaderWin::InitOnBackgroundThread() { | 413 void PolicyLoaderWin::InitOnBackgroundThread() { |
| 415 is_initialized_ = true; | 414 is_initialized_ = true; |
| 416 SetupWatches(); | 415 SetupWatches(); |
| 417 CollectEnterpriseUMAs(); | 416 CollectEnterpriseUMAs(); |
| 418 } | 417 } |
| 419 | 418 |
| 420 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { | 419 std::unique_ptr<PolicyBundle> PolicyLoaderWin::Load() { |
| 421 // Reset the watches BEFORE reading the individual policies to avoid | 420 // Reset the watches BEFORE reading the individual policies to avoid |
| 422 // missing a change notification. | 421 // missing a change notification. |
| 423 if (is_initialized_) | 422 if (is_initialized_) |
| 424 SetupWatches(); | 423 SetupWatches(); |
| 425 | 424 |
| 426 // Policy scope and corresponding hive. | 425 // Policy scope and corresponding hive. |
| 427 static const struct { | 426 static const struct { |
| 428 PolicyScope scope; | 427 PolicyScope scope; |
| 429 HKEY hive; | 428 HKEY hive; |
| 430 } kScopes[] = { | 429 } kScopes[] = { |
| 431 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, | 430 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, |
| 432 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, | 431 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, |
| 433 }; | 432 }; |
| 434 | 433 |
| 435 bool is_enterprise = base::win::IsEnrolledToDomain(); | 434 bool is_enterprise = base::win::IsEnrolledToDomain(); |
| 436 VLOG(1) << "Reading policy from the registry is " | 435 VLOG(1) << "Reading policy from the registry is " |
| 437 << (is_enterprise ? "enabled." : "disabled."); | 436 << (is_enterprise ? "enabled." : "disabled."); |
| 438 | 437 |
| 439 // Load policy data for the different scopes/levels and merge them. | 438 // Load policy data for the different scopes/levels and merge them. |
| 440 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); | 439 std::unique_ptr<PolicyBundle> bundle(new PolicyBundle()); |
| 441 PolicyMap* chrome_policy = | 440 PolicyMap* chrome_policy = |
| 442 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); | 441 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); |
| 443 for (size_t i = 0; i < arraysize(kScopes); ++i) { | 442 for (size_t i = 0; i < arraysize(kScopes); ++i) { |
| 444 PolicyScope scope = kScopes[i].scope; | 443 PolicyScope scope = kScopes[i].scope; |
| 445 PolicyLoadStatusSample status; | 444 PolicyLoadStatusSample status; |
| 446 RegistryDict gpo_dict; | 445 RegistryDict gpo_dict; |
| 447 | 446 |
| 448 // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and | 447 // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and |
| 449 // a matching LeaveCriticalPolicySection() call below after the | 448 // a matching LeaveCriticalPolicySection() call below after the |
| 450 // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be | 449 // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 463 // the additional effort this would introduce. | 462 // the additional effort this would introduce. |
| 464 | 463 |
| 465 bool is_registry_forced = is_enterprise || gpo_provider_ == nullptr; | 464 bool is_registry_forced = is_enterprise || gpo_provider_ == nullptr; |
| 466 if (is_registry_forced || !ReadPolicyFromGPO(scope, &gpo_dict, &status)) { | 465 if (is_registry_forced || !ReadPolicyFromGPO(scope, &gpo_dict, &status)) { |
| 467 VLOG_IF(1, !is_registry_forced) << "Failed to read GPO files for " | 466 VLOG_IF(1, !is_registry_forced) << "Failed to read GPO files for " |
| 468 << scope << " falling back to registry."; | 467 << scope << " falling back to registry."; |
| 469 gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_); | 468 gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_); |
| 470 } | 469 } |
| 471 | 470 |
| 472 // Remove special-cased entries from the GPO dictionary. | 471 // Remove special-cased entries from the GPO dictionary. |
| 473 scoped_ptr<RegistryDict> recommended_dict( | 472 std::unique_ptr<RegistryDict> recommended_dict( |
| 474 gpo_dict.RemoveKey(kKeyRecommended)); | 473 gpo_dict.RemoveKey(kKeyRecommended)); |
| 475 scoped_ptr<RegistryDict> third_party_dict( | 474 std::unique_ptr<RegistryDict> third_party_dict( |
| 476 gpo_dict.RemoveKey(kKeyThirdParty)); | 475 gpo_dict.RemoveKey(kKeyThirdParty)); |
| 477 | 476 |
| 478 // Load Chrome policy. | 477 // Load Chrome policy. |
| 479 LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy); | 478 LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy); |
| 480 LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope, | 479 LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope, |
| 481 chrome_policy); | 480 chrome_policy); |
| 482 | 481 |
| 483 // Load 3rd-party policy. | 482 // Load 3rd-party policy. |
| 484 if (third_party_dict) | 483 if (third_party_dict) |
| 485 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); | 484 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 684 | 683 |
| 685 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { | 684 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { |
| 686 DCHECK(object == user_policy_changed_event_.handle() || | 685 DCHECK(object == user_policy_changed_event_.handle() || |
| 687 object == machine_policy_changed_event_.handle()) | 686 object == machine_policy_changed_event_.handle()) |
| 688 << "unexpected object signaled policy reload, obj = " | 687 << "unexpected object signaled policy reload, obj = " |
| 689 << std::showbase << std::hex << object; | 688 << std::showbase << std::hex << object; |
| 690 Reload(false); | 689 Reload(false); |
| 691 } | 690 } |
| 692 | 691 |
| 693 } // namespace policy | 692 } // namespace policy |
| OLD | NEW |