| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/policy/policy_loader_win.h" | |
| 6 | |
| 7 #include <windows.h> | |
| 8 #include <rpc.h> // For struct GUID | |
| 9 #include <shlwapi.h> // For PathIsUNC() | |
| 10 #include <userenv.h> // For GPO functions | |
| 11 | |
| 12 #include <string> | |
| 13 #include <vector> | |
| 14 | |
| 15 // shlwapi.dll is required for PathIsUNC(). | |
| 16 #pragma comment(lib, "shlwapi.lib") | |
| 17 // userenv.dll is required for various GPO functions. | |
| 18 #pragma comment(lib, "userenv.lib") | |
| 19 | |
| 20 #include "base/basictypes.h" | |
| 21 #include "base/file_util.h" | |
| 22 #include "base/json/json_reader.h" | |
| 23 #include "base/lazy_instance.h" | |
| 24 #include "base/logging.h" | |
| 25 #include "base/scoped_native_library.h" | |
| 26 #include "base/sequenced_task_runner.h" | |
| 27 #include "base/stl_util.h" | |
| 28 #include "base/strings/string16.h" | |
| 29 #include "base/strings/string_util.h" | |
| 30 #include "chrome/browser/policy/policy_load_status.h" | |
| 31 #include "chrome/browser/policy/preg_parser_win.h" | |
| 32 #include "components/json_schema/json_schema_constants.h" | |
| 33 #include "components/policy/core/common/policy_bundle.h" | |
| 34 #include "components/policy/core/common/policy_map.h" | |
| 35 #include "components/policy/core/common/policy_namespace.h" | |
| 36 #include "components/policy/core/common/registry_dict_win.h" | |
| 37 #include "components/policy/core/common/schema.h" | |
| 38 | |
| 39 namespace schema = json_schema_constants; | |
| 40 | |
| 41 namespace policy { | |
| 42 | |
| 43 namespace { | |
| 44 | |
| 45 const char kKeyMandatory[] = "policy"; | |
| 46 const char kKeyRecommended[] = "recommended"; | |
| 47 const char kKeySchema[] = "schema"; | |
| 48 const char kKeyThirdParty[] = "3rdparty"; | |
| 49 | |
| 50 // The GUID of the registry settings group policy extension. | |
| 51 GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID; | |
| 52 | |
| 53 // A helper class encapsulating run-time-linked function calls to Wow64 APIs. | |
| 54 class Wow64Functions { | |
| 55 public: | |
| 56 Wow64Functions() | |
| 57 : kernel32_lib_(base::FilePath(L"kernel32")), | |
| 58 is_wow_64_process_(NULL), | |
| 59 wow_64_disable_wow_64_fs_redirection_(NULL), | |
| 60 wow_64_revert_wow_64_fs_redirection_(NULL) { | |
| 61 if (kernel32_lib_.is_valid()) { | |
| 62 is_wow_64_process_ = reinterpret_cast<IsWow64Process>( | |
| 63 kernel32_lib_.GetFunctionPointer("IsWow64Process")); | |
| 64 wow_64_disable_wow_64_fs_redirection_ = | |
| 65 reinterpret_cast<Wow64DisableWow64FSRedirection>( | |
| 66 kernel32_lib_.GetFunctionPointer( | |
| 67 "Wow64DisableWow64FsRedirection")); | |
| 68 wow_64_revert_wow_64_fs_redirection_ = | |
| 69 reinterpret_cast<Wow64RevertWow64FSRedirection>( | |
| 70 kernel32_lib_.GetFunctionPointer( | |
| 71 "Wow64RevertWow64FsRedirection")); | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 bool is_valid() { | |
| 76 return is_wow_64_process_ && | |
| 77 wow_64_disable_wow_64_fs_redirection_ && | |
| 78 wow_64_revert_wow_64_fs_redirection_; | |
| 79 } | |
| 80 | |
| 81 bool IsWow64() { | |
| 82 BOOL result = 0; | |
| 83 if (!is_wow_64_process_(GetCurrentProcess(), &result)) | |
| 84 PLOG(WARNING) << "IsWow64ProcFailed"; | |
| 85 return !!result; | |
| 86 } | |
| 87 | |
| 88 bool DisableFsRedirection(PVOID* previous_state) { | |
| 89 return !!wow_64_disable_wow_64_fs_redirection_(previous_state); | |
| 90 } | |
| 91 | |
| 92 bool RevertFsRedirection(PVOID previous_state) { | |
| 93 return !!wow_64_revert_wow_64_fs_redirection_(previous_state); | |
| 94 } | |
| 95 | |
| 96 private: | |
| 97 typedef BOOL (WINAPI* IsWow64Process)(HANDLE, PBOOL); | |
| 98 typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*); | |
| 99 typedef BOOL (WINAPI* Wow64RevertWow64FSRedirection)(PVOID); | |
| 100 | |
| 101 base::ScopedNativeLibrary kernel32_lib_; | |
| 102 | |
| 103 IsWow64Process is_wow_64_process_; | |
| 104 Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_; | |
| 105 Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_; | |
| 106 | |
| 107 DISALLOW_COPY_AND_ASSIGN(Wow64Functions); | |
| 108 }; | |
| 109 | |
| 110 // Global Wow64Function instance used by ScopedDisableWow64Redirection below. | |
| 111 static base::LazyInstance<Wow64Functions> g_wow_64_functions = | |
| 112 LAZY_INSTANCE_INITIALIZER; | |
| 113 | |
| 114 // Scoper that switches off Wow64 File System Redirection during its lifetime. | |
| 115 class ScopedDisableWow64Redirection { | |
| 116 public: | |
| 117 ScopedDisableWow64Redirection() | |
| 118 : active_(false), | |
| 119 previous_state_(NULL) { | |
| 120 Wow64Functions* wow64 = g_wow_64_functions.Pointer(); | |
| 121 if (wow64->is_valid() && wow64->IsWow64()) { | |
| 122 if (wow64->DisableFsRedirection(&previous_state_)) | |
| 123 active_ = true; | |
| 124 else | |
| 125 PLOG(WARNING) << "Wow64DisableWow64FSRedirection"; | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 ~ScopedDisableWow64Redirection() { | |
| 130 if (active_) | |
| 131 CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_)); | |
| 132 } | |
| 133 | |
| 134 bool is_active() { return active_; } | |
| 135 | |
| 136 private: | |
| 137 bool active_; | |
| 138 PVOID previous_state_; | |
| 139 | |
| 140 DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection); | |
| 141 }; | |
| 142 | |
| 143 // AppliedGPOListProvider implementation that calls actual Windows APIs. | |
| 144 class WinGPOListProvider : public AppliedGPOListProvider { | |
| 145 public: | |
| 146 virtual ~WinGPOListProvider() {} | |
| 147 | |
| 148 // AppliedGPOListProvider: | |
| 149 virtual DWORD GetAppliedGPOList(DWORD flags, | |
| 150 LPCTSTR machine_name, | |
| 151 PSID sid_user, | |
| 152 GUID* extension_guid, | |
| 153 PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE { | |
| 154 return ::GetAppliedGPOList(flags, machine_name, sid_user, extension_guid, | |
| 155 gpo_list); | |
| 156 } | |
| 157 | |
| 158 virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE { | |
| 159 return ::FreeGPOList(gpo_list); | |
| 160 } | |
| 161 }; | |
| 162 | |
| 163 // The default windows GPO list provider used for PolicyLoaderWin. | |
| 164 static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider = | |
| 165 LAZY_INSTANCE_INITIALIZER; | |
| 166 | |
| 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 | |
| 188 // settings to |policy| for the given |scope| and |level|. | |
| 189 void ParsePolicy(const RegistryDict* gpo_dict, | |
| 190 PolicyLevel level, | |
| 191 PolicyScope scope, | |
| 192 const base::DictionaryValue* schema, | |
| 193 PolicyMap* policy) { | |
| 194 if (!gpo_dict) | |
| 195 return; | |
| 196 | |
| 197 scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema)); | |
| 198 const base::DictionaryValue* policy_dict = NULL; | |
| 199 if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) { | |
| 200 LOG(WARNING) << "Root policy object is not a dictionary!"; | |
| 201 return; | |
| 202 } | |
| 203 | |
| 204 policy->LoadFrom(policy_dict, level, scope); | |
| 205 } | |
| 206 | |
| 207 } // namespace | |
| 208 | |
| 209 const base::FilePath::CharType PolicyLoaderWin::kPRegFileName[] = | |
| 210 FILE_PATH_LITERAL("Registry.pol"); | |
| 211 | |
| 212 PolicyLoaderWin::PolicyLoaderWin( | |
| 213 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
| 214 const string16& chrome_policy_key, | |
| 215 AppliedGPOListProvider* gpo_provider) | |
| 216 : AsyncPolicyLoader(task_runner), | |
| 217 is_initialized_(false), | |
| 218 chrome_policy_key_(chrome_policy_key), | |
| 219 gpo_provider_(gpo_provider), | |
| 220 user_policy_changed_event_(false, false), | |
| 221 machine_policy_changed_event_(false, false), | |
| 222 user_policy_watcher_failed_(false), | |
| 223 machine_policy_watcher_failed_(false) { | |
| 224 if (!::RegisterGPNotification(user_policy_changed_event_.handle(), false)) { | |
| 225 DPLOG(WARNING) << "Failed to register user group policy notification"; | |
| 226 user_policy_watcher_failed_ = true; | |
| 227 } | |
| 228 if (!::RegisterGPNotification(machine_policy_changed_event_.handle(), true)) { | |
| 229 DPLOG(WARNING) << "Failed to register machine group policy notification."; | |
| 230 machine_policy_watcher_failed_ = true; | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 PolicyLoaderWin::~PolicyLoaderWin() { | |
| 235 if (!user_policy_watcher_failed_) { | |
| 236 ::UnregisterGPNotification(user_policy_changed_event_.handle()); | |
| 237 user_policy_watcher_.StopWatching(); | |
| 238 } | |
| 239 if (!machine_policy_watcher_failed_) { | |
| 240 ::UnregisterGPNotification(machine_policy_changed_event_.handle()); | |
| 241 machine_policy_watcher_.StopWatching(); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 // static | |
| 246 scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create( | |
| 247 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
| 248 const string16& chrome_policy_key) { | |
| 249 return make_scoped_ptr( | |
| 250 new PolicyLoaderWin(task_runner, | |
| 251 chrome_policy_key, | |
| 252 g_win_gpo_list_provider.Pointer())); | |
| 253 } | |
| 254 | |
| 255 void PolicyLoaderWin::InitOnBackgroundThread() { | |
| 256 is_initialized_ = true; | |
| 257 SetupWatches(); | |
| 258 } | |
| 259 | |
| 260 scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() { | |
| 261 // Reset the watches BEFORE reading the individual policies to avoid | |
| 262 // missing a change notification. | |
| 263 if (is_initialized_) | |
| 264 SetupWatches(); | |
| 265 | |
| 266 if (chrome_policy_schema_.empty()) | |
| 267 BuildChromePolicySchema(); | |
| 268 | |
| 269 // Policy scope and corresponding hive. | |
| 270 static const struct { | |
| 271 PolicyScope scope; | |
| 272 HKEY hive; | |
| 273 } kScopes[] = { | |
| 274 { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE }, | |
| 275 { POLICY_SCOPE_USER, HKEY_CURRENT_USER }, | |
| 276 }; | |
| 277 | |
| 278 // Load policy data for the different scopes/levels and merge them. | |
| 279 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); | |
| 280 PolicyMap* chrome_policy = | |
| 281 &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); | |
| 282 for (size_t i = 0; i < arraysize(kScopes); ++i) { | |
| 283 PolicyScope scope = kScopes[i].scope; | |
| 284 PolicyLoadStatusSample status; | |
| 285 RegistryDict gpo_dict; | |
| 286 | |
| 287 // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and | |
| 288 // a matching LeaveCriticalPolicySection() call below after the | |
| 289 // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be | |
| 290 // unavailable for extended periods of time, and there are reports of this | |
| 291 // happening in the wild: http://crbug.com/265862. | |
| 292 // | |
| 293 // Blocking for minutes is neither acceptable for Chrome startup, nor on | |
| 294 // the FILE thread on which this code runs in steady state. Given that | |
| 295 // there have never been any reports of issues due to partially-applied / | |
| 296 // corrupt group policy, this code intentionally omits the | |
| 297 // EnterCriticalPolicySection() call. | |
| 298 // | |
| 299 // If there's ever reason to revisit this decision, one option could be to | |
| 300 // make the EnterCriticalPolicySection() call on a dedicated thread and | |
| 301 // timeout on it more aggressively. For now, there's no justification for | |
| 302 // the additional effort this would introduce. | |
| 303 | |
| 304 if (!ReadPolicyFromGPO(scope, &gpo_dict, &status)) { | |
| 305 VLOG(1) << "Failed to read GPO files for " << scope | |
| 306 << " falling back to registry."; | |
| 307 gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_); | |
| 308 } | |
| 309 | |
| 310 // Remove special-cased entries from the GPO dictionary. | |
| 311 scoped_ptr<RegistryDict> recommended_dict( | |
| 312 gpo_dict.RemoveKey(kKeyRecommended)); | |
| 313 scoped_ptr<RegistryDict> third_party_dict( | |
| 314 gpo_dict.RemoveKey(kKeyThirdParty)); | |
| 315 | |
| 316 // Load Chrome policy. | |
| 317 LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy); | |
| 318 LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope, | |
| 319 chrome_policy); | |
| 320 | |
| 321 // Load 3rd-party policy. | |
| 322 if (third_party_dict) | |
| 323 Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get()); | |
| 324 } | |
| 325 | |
| 326 return bundle.Pass(); | |
| 327 } | |
| 328 | |
| 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, | |
| 360 RegistryDict* policy, | |
| 361 PolicyLoadStatusSample* status) { | |
| 362 // 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 | |
| 364 // process on a 64-bit system, in which case Wow64 FS redirection redirects | |
| 365 // access to the %WINDIR%/System32/GroupPolicy directory to | |
| 366 // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the | |
| 367 // system-native directory. | |
| 368 if (base::PathExists(preg_file)) { | |
| 369 return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy, status); | |
| 370 } else { | |
| 371 // Try with redirection switched off. | |
| 372 ScopedDisableWow64Redirection redirection_disable; | |
| 373 if (redirection_disable.is_active() && base::PathExists(preg_file)) { | |
| 374 status->Add(POLICY_LOAD_STATUS_WOW64_REDIRECTION_DISABLED); | |
| 375 return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy, | |
| 376 status); | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 // Report the error. | |
| 381 LOG(ERROR) << "PReg file doesn't exist: " << preg_file.value(); | |
| 382 status->Add(POLICY_LOAD_STATUS_MISSING); | |
| 383 return false; | |
| 384 } | |
| 385 | |
| 386 bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope, | |
| 387 PGROUP_POLICY_OBJECT policy_object_list, | |
| 388 RegistryDict* policy, | |
| 389 PolicyLoadStatusSample* status) { | |
| 390 RegistryDict parsed_policy; | |
| 391 RegistryDict forced_policy; | |
| 392 for (GROUP_POLICY_OBJECT* policy_object = policy_object_list; | |
| 393 policy_object; policy_object = policy_object->pNext) { | |
| 394 if (policy_object->dwOptions & GPO_FLAG_DISABLE) | |
| 395 continue; | |
| 396 | |
| 397 if (PathIsUNC(policy_object->lpFileSysPath)) { | |
| 398 // UNC path: Assume this is an AD-managed machine, which updates the | |
| 399 // registry via GPO's standard registry CSE periodically. Fall back to | |
| 400 // reading from the registry in this case. | |
| 401 status->Add(POLICY_LOAD_STATUS_INACCCESSIBLE); | |
| 402 return false; | |
| 403 } | |
| 404 | |
| 405 base::FilePath preg_file_path( | |
| 406 base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName)); | |
| 407 if (policy_object->dwOptions & GPO_FLAG_FORCE) { | |
| 408 RegistryDict new_forced_policy; | |
| 409 if (!ReadPRegFile(preg_file_path, &new_forced_policy, status)) | |
| 410 return false; | |
| 411 | |
| 412 // Merge with existing forced policy, giving precedence to the existing | |
| 413 // forced policy. | |
| 414 new_forced_policy.Merge(forced_policy); | |
| 415 forced_policy.Swap(&new_forced_policy); | |
| 416 } else { | |
| 417 if (!ReadPRegFile(preg_file_path, &parsed_policy, status)) | |
| 418 return false; | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 // Merge, give precedence to forced policy. | |
| 423 parsed_policy.Merge(forced_policy); | |
| 424 policy->Swap(&parsed_policy); | |
| 425 | |
| 426 return true; | |
| 427 } | |
| 428 | |
| 429 bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope, | |
| 430 RegistryDict* policy, | |
| 431 PolicyLoadStatusSample* status) { | |
| 432 PGROUP_POLICY_OBJECT policy_object_list = NULL; | |
| 433 DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0; | |
| 434 if (gpo_provider_->GetAppliedGPOList( | |
| 435 flags, NULL, NULL, &kRegistrySettingsCSEGUID, | |
| 436 &policy_object_list) != ERROR_SUCCESS) { | |
| 437 PLOG(ERROR) << "GetAppliedGPOList scope " << scope; | |
| 438 status->Add(POLICY_LOAD_STATUS_QUERY_FAILED); | |
| 439 return false; | |
| 440 } | |
| 441 | |
| 442 bool result = true; | |
| 443 if (policy_object_list) { | |
| 444 result = LoadGPOPolicy(scope, policy_object_list, policy, status); | |
| 445 if (!gpo_provider_->FreeGPOList(policy_object_list)) | |
| 446 LOG(WARNING) << "FreeGPOList"; | |
| 447 } else { | |
| 448 status->Add(POLICY_LOAD_STATUS_NO_POLICY); | |
| 449 } | |
| 450 | |
| 451 return result; | |
| 452 } | |
| 453 | |
| 454 void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict, | |
| 455 PolicyLevel level, | |
| 456 PolicyScope scope, | |
| 457 PolicyMap* chrome_policy_map) { | |
| 458 PolicyMap policy; | |
| 459 ParsePolicy(gpo_dict, level, scope, &chrome_policy_schema_, &policy); | |
| 460 chrome_policy_map->MergeFrom(policy); | |
| 461 } | |
| 462 | |
| 463 void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict, | |
| 464 PolicyScope scope, | |
| 465 PolicyBundle* bundle) { | |
| 466 // Map of known 3rd party policy domain name to their enum values. | |
| 467 static const struct { | |
| 468 const char* name; | |
| 469 PolicyDomain domain; | |
| 470 } k3rdPartyDomains[] = { | |
| 471 { "extensions", POLICY_DOMAIN_EXTENSIONS }, | |
| 472 }; | |
| 473 | |
| 474 // Policy level and corresponding path. | |
| 475 static const struct { | |
| 476 PolicyLevel level; | |
| 477 const char* path; | |
| 478 } kLevels[] = { | |
| 479 { POLICY_LEVEL_MANDATORY, kKeyMandatory }, | |
| 480 { POLICY_LEVEL_RECOMMENDED, kKeyRecommended }, | |
| 481 }; | |
| 482 | |
| 483 for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) { | |
| 484 const char* name = k3rdPartyDomains[i].name; | |
| 485 const PolicyDomain domain = k3rdPartyDomains[i].domain; | |
| 486 const RegistryDict* domain_dict = gpo_dict->GetKey(name); | |
| 487 if (!domain_dict) | |
| 488 continue; | |
| 489 | |
| 490 for (RegistryDict::KeyMap::const_iterator component( | |
| 491 domain_dict->keys().begin()); | |
| 492 component != domain_dict->keys().end(); | |
| 493 ++component) { | |
| 494 // Load the schema. | |
| 495 const base::DictionaryValue* schema_dict = NULL; | |
| 496 scoped_ptr<base::Value> schema; | |
| 497 std::string schema_json; | |
| 498 const base::Value* schema_value = component->second->GetValue(kKeySchema); | |
| 499 if (schema_value && schema_value->GetAsString(&schema_json)) { | |
| 500 schema.reset(base::JSONReader::Read(schema_json)); | |
| 501 if (!schema || !schema->GetAsDictionary(&schema_dict)) { | |
| 502 LOG(WARNING) << "Failed to parse 3rd-part policy schema for " | |
| 503 << domain << "/" << component->first; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 // Parse policy. | |
| 508 for (size_t j = 0; j < arraysize(kLevels); j++) { | |
| 509 const RegistryDict* policy_dict = | |
| 510 component->second->GetKey(kLevels[j].path); | |
| 511 if (!policy_dict) | |
| 512 continue; | |
| 513 | |
| 514 PolicyMap policy; | |
| 515 ParsePolicy(policy_dict, kLevels[j].level, scope, schema_dict, &policy); | |
| 516 PolicyNamespace policy_namespace(domain, component->first); | |
| 517 bundle->Get(policy_namespace).MergeFrom(policy); | |
| 518 } | |
| 519 } | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 void PolicyLoaderWin::SetupWatches() { | |
| 524 DCHECK(is_initialized_); | |
| 525 if (!user_policy_watcher_failed_ && | |
| 526 !user_policy_watcher_.GetWatchedObject() && | |
| 527 !user_policy_watcher_.StartWatching( | |
| 528 user_policy_changed_event_.handle(), this)) { | |
| 529 DLOG(WARNING) << "Failed to start watch for user policy change event"; | |
| 530 user_policy_watcher_failed_ = true; | |
| 531 } | |
| 532 if (!machine_policy_watcher_failed_ && | |
| 533 !machine_policy_watcher_.GetWatchedObject() && | |
| 534 !machine_policy_watcher_.StartWatching( | |
| 535 machine_policy_changed_event_.handle(), this)) { | |
| 536 DLOG(WARNING) << "Failed to start watch for machine policy change event"; | |
| 537 machine_policy_watcher_failed_ = true; | |
| 538 } | |
| 539 } | |
| 540 | |
| 541 void PolicyLoaderWin::OnObjectSignaled(HANDLE object) { | |
| 542 DCHECK(object == user_policy_changed_event_.handle() || | |
| 543 object == machine_policy_changed_event_.handle()) | |
| 544 << "unexpected object signaled policy reload, obj = " | |
| 545 << std::showbase << std::hex << object; | |
| 546 Reload(false); | |
| 547 } | |
| 548 | |
| 549 } // namespace policy | |
| OLD | NEW |