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
3// found in the LICENSE file.
4
5#include "components/policy/core/common/policy_loader_win.h"
6
7#include <windows.h>
8#include <lm.h>       // For limits.
9#include <ntdsapi.h>  // For Ds[Un]Bind
10#include <rpc.h>      // For struct GUID
11#include <shlwapi.h>  // For PathIsUNC()
12#include <userenv.h>  // For GPO functions
13
14#include <string>
15#include <vector>
16
17// shlwapi.dll is required for PathIsUNC().
18#pragma comment(lib, "shlwapi.lib")
19// userenv.dll is required for various GPO functions.
20#pragma comment(lib, "userenv.lib")
21// ntdsapi.dll is required for Ds[Un]Bind calls.
22#pragma comment(lib, "ntdsapi.lib")
23
24#include "base/basictypes.h"
25#include "base/bind.h"
26#include "base/file_util.h"
27#include "base/json/json_reader.h"
28#include "base/json/json_writer.h"
29#include "base/lazy_instance.h"
30#include "base/logging.h"
31#include "base/memory/scoped_ptr.h"
32#include "base/metrics/histogram.h"
33#include "base/scoped_native_library.h"
34#include "base/sequenced_task_runner.h"
35#include "base/stl_util.h"
36#include "base/strings/string16.h"
37#include "base/strings/string_util.h"
38#include "base/values.h"
39#include "base/win/win_util.h"
40#include "base/win/windows_version.h"
41#include "components/json_schema/json_schema_constants.h"
42#include "components/policy/core/common/policy_bundle.h"
43#include "components/policy/core/common/policy_load_status.h"
44#include "components/policy/core/common/policy_map.h"
45#include "components/policy/core/common/policy_namespace.h"
46#include "components/policy/core/common/preg_parser_win.h"
47#include "components/policy/core/common/registry_dict_win.h"
48#include "components/policy/core/common/schema.h"
49#include "policy/policy_constants.h"
50
51namespace schema = json_schema_constants;
52
53namespace policy {
54
55namespace {
56
57const char kKeyMandatory[] = "policy";
58const char kKeyRecommended[] = "recommended";
59const char kKeySchema[] = "schema";
60const char kKeyThirdParty[] = "3rdparty";
61
62// The Legacy Browser Support was the first user of the policy-for-extensions
63// API, and relied on behavior that will be phased out. If this extension is
64// present then its policies will be loaded in a special way.
65// TODO(joaodasilva): remove this for M35. http://crbug.com/325349
66const char kLegacyBrowserSupportExtensionId[] =
67    "heildphpnddilhkemkielfhnkaagiabh";
68
69// The web store url that is the only trusted source for extensions.
70const char kExpectedWebStoreUrl[] =
71    ";https://clients2.google.com/service/update2/crx";
72// String to be prepended to each blocked entry.
73const char kBlockedExtensionPrefix[] = "[BLOCKED]";
74
75// The GUID of the registry settings group policy extension.
76GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID;
77
78// The list of possible errors that can occur while collecting information about
79// the current enterprise environment.
80enum DomainCheckErrors {
81  DOMAIN_CHECK_ERROR_GET_JOIN_INFO = 0,
82  DOMAIN_CHECK_ERROR_DS_BIND,
83  DOMAIN_CHECK_ERROR_LAST,
84};
85
86// If the LBS extension is found and contains a schema in the registry then this
87// function is used to patch it, and make it compliant. The fix is to
88// add an "items" attribute to lists that don't declare it.
89std::string PatchSchema(const std::string& schema) {
90  base::JSONParserOptions options = base::JSON_PARSE_RFC;
91  scoped_ptr<base::Value> json(base::JSONReader::Read(schema, options));
92  base::DictionaryValue* dict = NULL;
93  base::DictionaryValue* properties = NULL;
94  if (!json ||
95      !json->GetAsDictionary(&dict) ||
96      !dict->GetDictionary(schema::kProperties, &properties)) {
97    return schema;
98  }
99
100  for (base::DictionaryValue::Iterator it(*properties);
101       !it.IsAtEnd(); it.Advance()) {
102    base::DictionaryValue* policy_schema = NULL;
103    std::string type;
104    if (properties->GetDictionary(it.key(), &policy_schema) &&
105        policy_schema->GetString(schema::kType, &type) &&
106        type == schema::kArray &&
107        !policy_schema->HasKey(schema::kItems)) {
108      scoped_ptr<base::DictionaryValue> items(new base::DictionaryValue());
109      items->SetString(schema::kType, schema::kString);
110      policy_schema->Set(schema::kItems, items.release());
111    }
112  }
113
114  std::string serialized;
115  base::JSONWriter::Write(json.get(), &serialized);
116  return serialized;
117}
118
119// Verifies that untrusted policies contain only safe values. Modifies the
120// |policy| in place.
121void FilterUntrustedPolicy(PolicyMap* policy) {
122  if (base::win::IsEnrolledToDomain())
123    return;
124
125  const PolicyMap::Entry* map_entry =
126      policy->Get(policy::key::kExtensionInstallForcelist);
127  if (map_entry && map_entry->value) {
128    int invalid_policies = 0;
129    const base::ListValue* policy_list_value = NULL;
130    if (!map_entry->value->GetAsList(&policy_list_value))
131      return;
132
133    scoped_ptr<base::ListValue> filtered_values(new base::ListValue);
134    for (base::ListValue::const_iterator list_entry(policy_list_value->begin());
135         list_entry != policy_list_value->end(); ++list_entry) {
136      std::string entry;
137      if (!(*list_entry)->GetAsString(&entry))
138        continue;
139      size_t pos = entry.find(';');
140      if (pos == std::string::npos)
141        continue;
142      // Only allow custom update urls in enterprise environments.
143      if (!LowerCaseEqualsASCII(entry.substr(pos), kExpectedWebStoreUrl)) {
144        entry = kBlockedExtensionPrefix + entry;
145        invalid_policies++;
146      }
147
148      filtered_values->AppendString(entry);
149    }
150    policy->Set(policy::key::kExtensionInstallForcelist,
151                map_entry->level, map_entry->scope,
152                filtered_values.release(),
153                map_entry->external_data_fetcher);
154    UMA_HISTOGRAM_COUNTS("EnterpriseCheck.InvalidPoliciesDetected",
155                         invalid_policies);
156  }
157}
158
159// A helper class encapsulating run-time-linked function calls to Wow64 APIs.
160class Wow64Functions {
161 public:
162  Wow64Functions()
163    : kernel32_lib_(base::FilePath(L"kernel32")),
164      is_wow_64_process_(NULL),
165      wow_64_disable_wow_64_fs_redirection_(NULL),
166      wow_64_revert_wow_64_fs_redirection_(NULL) {
167    if (kernel32_lib_.is_valid()) {
168      is_wow_64_process_ = reinterpret_cast<IsWow64Process>(
169          kernel32_lib_.GetFunctionPointer("IsWow64Process"));
170      wow_64_disable_wow_64_fs_redirection_ =
171          reinterpret_cast<Wow64DisableWow64FSRedirection>(
172              kernel32_lib_.GetFunctionPointer(
173                  "Wow64DisableWow64FsRedirection"));
174      wow_64_revert_wow_64_fs_redirection_ =
175          reinterpret_cast<Wow64RevertWow64FSRedirection>(
176              kernel32_lib_.GetFunctionPointer(
177                  "Wow64RevertWow64FsRedirection"));
178    }
179  }
180
181  bool is_valid() {
182    return is_wow_64_process_ &&
183        wow_64_disable_wow_64_fs_redirection_ &&
184        wow_64_revert_wow_64_fs_redirection_;
185 }
186
187  bool IsWow64() {
188    BOOL result = 0;
189    if (!is_wow_64_process_(GetCurrentProcess(), &result))
190      PLOG(WARNING) << "IsWow64ProcFailed";
191    return !!result;
192  }
193
194  bool DisableFsRedirection(PVOID* previous_state) {
195    return !!wow_64_disable_wow_64_fs_redirection_(previous_state);
196  }
197
198  bool RevertFsRedirection(PVOID previous_state) {
199    return !!wow_64_revert_wow_64_fs_redirection_(previous_state);
200  }
201
202 private:
203  typedef BOOL (WINAPI* IsWow64Process)(HANDLE, PBOOL);
204  typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*);
205  typedef BOOL (WINAPI* Wow64RevertWow64FSRedirection)(PVOID);
206
207  base::ScopedNativeLibrary kernel32_lib_;
208
209  IsWow64Process is_wow_64_process_;
210  Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_;
211  Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_;
212
213  DISALLOW_COPY_AND_ASSIGN(Wow64Functions);
214};
215
216// Global Wow64Function instance used by ScopedDisableWow64Redirection below.
217static base::LazyInstance<Wow64Functions> g_wow_64_functions =
218    LAZY_INSTANCE_INITIALIZER;
219
220// Scoper that switches off Wow64 File System Redirection during its lifetime.
221class ScopedDisableWow64Redirection {
222 public:
223  ScopedDisableWow64Redirection()
224    : active_(false),
225      previous_state_(NULL) {
226    Wow64Functions* wow64 = g_wow_64_functions.Pointer();
227    if (wow64->is_valid() && wow64->IsWow64()) {
228      if (wow64->DisableFsRedirection(&previous_state_))
229        active_ = true;
230      else
231        PLOG(WARNING) << "Wow64DisableWow64FSRedirection";
232    }
233  }
234
235  ~ScopedDisableWow64Redirection() {
236    if (active_)
237      CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_));
238  }
239
240  bool is_active() { return active_; }
241
242 private:
243  bool active_;
244  PVOID previous_state_;
245
246  DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection);
247};
248
249// AppliedGPOListProvider implementation that calls actual Windows APIs.
250class WinGPOListProvider : public AppliedGPOListProvider {
251 public:
252  virtual ~WinGPOListProvider() {}
253
254  // AppliedGPOListProvider:
255  virtual DWORD GetAppliedGPOList(DWORD flags,
256                                  LPCTSTR machine_name,
257                                  PSID sid_user,
258                                  GUID* extension_guid,
259                                  PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE {
260    return ::GetAppliedGPOList(flags, machine_name, sid_user, extension_guid,
261                               gpo_list);
262  }
263
264  virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE {
265    return ::FreeGPOList(gpo_list);
266  }
267};
268
269// The default windows GPO list provider used for PolicyLoaderWin.
270static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider =
271    LAZY_INSTANCE_INITIALIZER;
272
273// Parses |gpo_dict| according to |schema| and writes the resulting policy
274// settings to |policy| for the given |scope| and |level|.
275void ParsePolicy(const RegistryDict* gpo_dict,
276                 PolicyLevel level,
277                 PolicyScope scope,
278                 const Schema& schema,
279                 PolicyMap* policy) {
280  if (!gpo_dict)
281    return;
282
283  scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema));
284  const base::DictionaryValue* policy_dict = NULL;
285  if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) {
286    LOG(WARNING) << "Root policy object is not a dictionary!";
287    return;
288  }
289
290  policy->LoadFrom(policy_dict, level, scope);
291}
292
293// Collects stats about the enterprise environment that can be used to decide
294// how to parse the existing policy information.
295void CollectEnterpriseUMAs() {
296  // Collect statistics about the windows suite.
297  UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.OSType",
298                            base::win::OSInfo::GetInstance()->version_type(),
299                            base::win::SUITE_LAST);
300
301  // Get the computer's domain status.
302  LPWSTR domain;
303  NETSETUP_JOIN_STATUS join_status;
304  if (NERR_Success != ::NetGetJoinInformation(NULL, &domain, &join_status)) {
305    UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed",
306                              DOMAIN_CHECK_ERROR_GET_JOIN_INFO,
307                              DOMAIN_CHECK_ERROR_LAST);
308    return;
309  }
310  ::NetApiBufferFree(domain);
311
312  bool in_domain = join_status == NetSetupDomainName;
313  UMA_HISTOGRAM_BOOLEAN("EnterpriseCheck.InDomain", in_domain);
314  if (in_domain) {
315    // This check will tell us how often are domain computers actually
316    // connected to the enterprise network while Chrome is running.
317    HANDLE server_bind;
318    if (ERROR_SUCCESS == ::DsBind(NULL, NULL, &server_bind)) {
319      UMA_HISTOGRAM_COUNTS("EnterpriseCheck.DomainBindSucceeded", 1);
320      ::DsUnBind(&server_bind);
321    } else {
322      UMA_HISTOGRAM_ENUMERATION("EnterpriseCheck.DomainCheckFailed",
323                                DOMAIN_CHECK_ERROR_DS_BIND,
324                                DOMAIN_CHECK_ERROR_LAST);
325    }
326  }
327}
328
329}  // namespace
330
331const base::FilePath::CharType PolicyLoaderWin::kPRegFileName[] =
332    FILE_PATH_LITERAL("Registry.pol");
333
334PolicyLoaderWin::PolicyLoaderWin(
335    scoped_refptr<base::SequencedTaskRunner> task_runner,
336    const base::string16& chrome_policy_key,
337    AppliedGPOListProvider* gpo_provider)
338    : AsyncPolicyLoader(task_runner),
339      is_initialized_(false),
340      chrome_policy_key_(chrome_policy_key),
341      gpo_provider_(gpo_provider),
342      user_policy_changed_event_(false, false),
343      machine_policy_changed_event_(false, false),
344      user_policy_watcher_failed_(false),
345      machine_policy_watcher_failed_(false) {
346  if (!::RegisterGPNotification(user_policy_changed_event_.handle(), false)) {
347    DPLOG(WARNING) << "Failed to register user group policy notification";
348    user_policy_watcher_failed_ = true;
349  }
350  if (!::RegisterGPNotification(machine_policy_changed_event_.handle(), true)) {
351    DPLOG(WARNING) << "Failed to register machine group policy notification.";
352    machine_policy_watcher_failed_ = true;
353  }
354}
355
356PolicyLoaderWin::~PolicyLoaderWin() {
357  if (!user_policy_watcher_failed_) {
358    ::UnregisterGPNotification(user_policy_changed_event_.handle());
359    user_policy_watcher_.StopWatching();
360  }
361  if (!machine_policy_watcher_failed_) {
362    ::UnregisterGPNotification(machine_policy_changed_event_.handle());
363    machine_policy_watcher_.StopWatching();
364  }
365}
366
367// static
368scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create(
369    scoped_refptr<base::SequencedTaskRunner> task_runner,
370    const base::string16& chrome_policy_key) {
371  return make_scoped_ptr(
372      new PolicyLoaderWin(task_runner,
373                          chrome_policy_key,
374                          g_win_gpo_list_provider.Pointer()));
375}
376
377void PolicyLoaderWin::InitOnBackgroundThread() {
378  is_initialized_ = true;
379  SetupWatches();
380  CollectEnterpriseUMAs();
381}
382
383scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() {
384  // Reset the watches BEFORE reading the individual policies to avoid
385  // missing a change notification.
386  if (is_initialized_)
387    SetupWatches();
388
389  // Policy scope and corresponding hive.
390  static const struct {
391    PolicyScope scope;
392    HKEY hive;
393  } kScopes[] = {
394    { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE },
395    { POLICY_SCOPE_USER,    HKEY_CURRENT_USER  },
396  };
397
398  bool is_enterprise = base::win::IsEnrolledToDomain();
399  VLOG(1) << "Reading policy from the registry is "
400          << (is_enterprise ? "enabled." : "disabled.");
401
402  // Load policy data for the different scopes/levels and merge them.
403  scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
404  PolicyMap* chrome_policy =
405      &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
406  for (size_t i = 0; i < arraysize(kScopes); ++i) {
407    PolicyScope scope = kScopes[i].scope;
408    PolicyLoadStatusSample status;
409    RegistryDict gpo_dict;
410
411    // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and
412    // a matching LeaveCriticalPolicySection() call below after the
413    // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be
414    // unavailable for extended periods of time, and there are reports of this
415    // happening in the wild: http://crbug.com/265862.
416    //
417    // Blocking for minutes is neither acceptable for Chrome startup, nor on
418    // the FILE thread on which this code runs in steady state. Given that
419    // there have never been any reports of issues due to partially-applied /
420    // corrupt group policy, this code intentionally omits the
421    // EnterCriticalPolicySection() call.
422    //
423    // If there's ever reason to revisit this decision, one option could be to
424    // make the EnterCriticalPolicySection() call on a dedicated thread and
425    // timeout on it more aggressively. For now, there's no justification for
426    // the additional effort this would introduce.
427
428    if (is_enterprise || !ReadPolicyFromGPO(scope, &gpo_dict, &status)) {
429      VLOG_IF(1, !is_enterprise) << "Failed to read GPO files for " << scope
430                                 << " falling back to registry.";
431      gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_);
432    }
433
434    // Remove special-cased entries from the GPO dictionary.
435    scoped_ptr<RegistryDict> recommended_dict(
436        gpo_dict.RemoveKey(kKeyRecommended));
437    scoped_ptr<RegistryDict> third_party_dict(
438        gpo_dict.RemoveKey(kKeyThirdParty));
439
440    // Load Chrome policy.
441    LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy);
442    LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope,
443                     chrome_policy);
444
445    // Load 3rd-party policy.
446    if (third_party_dict)
447      Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get());
448  }
449
450  return bundle.Pass();
451}
452
453bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file,
454                                   RegistryDict* policy,
455                                   PolicyLoadStatusSample* status) {
456  // The following deals with the minor annoyance that Wow64 FS redirection
457  // might need to be turned off: This is the case if running as a 32-bit
458  // process on a 64-bit system, in which case Wow64 FS redirection redirects
459  // access to the %WINDIR%/System32/GroupPolicy directory to
460  // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the
461  // system-native directory.
462  if (base::PathExists(preg_file)) {
463    return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy, status);
464  } else {
465    // Try with redirection switched off.
466    ScopedDisableWow64Redirection redirection_disable;
467    if (redirection_disable.is_active() && base::PathExists(preg_file)) {
468      status->Add(POLICY_LOAD_STATUS_WOW64_REDIRECTION_DISABLED);
469      return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy,
470                                   status);
471    }
472  }
473
474  // Report the error.
475  LOG(ERROR) << "PReg file doesn't exist: " << preg_file.value();
476  status->Add(POLICY_LOAD_STATUS_MISSING);
477  return false;
478}
479
480bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope,
481                                    PGROUP_POLICY_OBJECT policy_object_list,
482                                    RegistryDict* policy,
483                                    PolicyLoadStatusSample* status) {
484  RegistryDict parsed_policy;
485  RegistryDict forced_policy;
486  for (GROUP_POLICY_OBJECT* policy_object = policy_object_list;
487       policy_object; policy_object = policy_object->pNext) {
488    if (policy_object->dwOptions & GPO_FLAG_DISABLE)
489      continue;
490
491    if (PathIsUNC(policy_object->lpFileSysPath)) {
492      // UNC path: Assume this is an AD-managed machine, which updates the
493      // registry via GPO's standard registry CSE periodically. Fall back to
494      // reading from the registry in this case.
495      status->Add(POLICY_LOAD_STATUS_INACCCESSIBLE);
496      return false;
497    }
498
499    base::FilePath preg_file_path(
500        base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName));
501    if (policy_object->dwOptions & GPO_FLAG_FORCE) {
502      RegistryDict new_forced_policy;
503      if (!ReadPRegFile(preg_file_path, &new_forced_policy, status))
504        return false;
505
506      // Merge with existing forced policy, giving precedence to the existing
507      // forced policy.
508      new_forced_policy.Merge(forced_policy);
509      forced_policy.Swap(&new_forced_policy);
510    } else {
511      if (!ReadPRegFile(preg_file_path, &parsed_policy, status))
512        return false;
513    }
514  }
515
516  // Merge, give precedence to forced policy.
517  parsed_policy.Merge(forced_policy);
518  policy->Swap(&parsed_policy);
519
520  return true;
521}
522
523bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope,
524                                        RegistryDict* policy,
525                                        PolicyLoadStatusSample* status) {
526  PGROUP_POLICY_OBJECT policy_object_list = NULL;
527  DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0;
528  if (gpo_provider_->GetAppliedGPOList(
529          flags, NULL, NULL, &kRegistrySettingsCSEGUID,
530          &policy_object_list) != ERROR_SUCCESS) {
531    PLOG(ERROR) << "GetAppliedGPOList scope " << scope;
532    status->Add(POLICY_LOAD_STATUS_QUERY_FAILED);
533    return false;
534  }
535
536  bool result = true;
537  if (policy_object_list) {
538    result = LoadGPOPolicy(scope, policy_object_list, policy, status);
539    if (!gpo_provider_->FreeGPOList(policy_object_list))
540      LOG(WARNING) << "FreeGPOList";
541  } else {
542    status->Add(POLICY_LOAD_STATUS_NO_POLICY);
543  }
544
545  return result;
546}
547
548void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict,
549                                       PolicyLevel level,
550                                       PolicyScope scope,
551                                       PolicyMap* chrome_policy_map) {
552  PolicyMap policy;
553  const Schema* chrome_schema =
554      schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
555  ParsePolicy(gpo_dict, level, scope, *chrome_schema, &policy);
556  FilterUntrustedPolicy(&policy);
557  chrome_policy_map->MergeFrom(policy);
558}
559
560void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict,
561                                         PolicyScope scope,
562                                         PolicyBundle* bundle) {
563  // Map of known 3rd party policy domain name to their enum values.
564  static const struct {
565    const char* name;
566    PolicyDomain domain;
567  } k3rdPartyDomains[] = {
568    { "extensions", POLICY_DOMAIN_EXTENSIONS },
569  };
570
571  // Policy level and corresponding path.
572  static const struct {
573    PolicyLevel level;
574    const char* path;
575  } kLevels[] = {
576    { POLICY_LEVEL_MANDATORY,   kKeyMandatory   },
577    { POLICY_LEVEL_RECOMMENDED, kKeyRecommended },
578  };
579
580  for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) {
581    const char* name = k3rdPartyDomains[i].name;
582    const PolicyDomain domain = k3rdPartyDomains[i].domain;
583    const RegistryDict* domain_dict = gpo_dict->GetKey(name);
584    if (!domain_dict)
585      continue;
586
587    for (RegistryDict::KeyMap::const_iterator component(
588             domain_dict->keys().begin());
589         component != domain_dict->keys().end();
590         ++component) {
591      const PolicyNamespace policy_namespace(domain, component->first);
592
593      const Schema* schema_from_map = schema_map()->GetSchema(policy_namespace);
594      if (!schema_from_map) {
595        // This extension isn't installed or doesn't support policies.
596        continue;
597      }
598      Schema schema = *schema_from_map;
599
600      if (!schema.valid() &&
601          policy_namespace.domain == POLICY_DOMAIN_EXTENSIONS &&
602          policy_namespace.component_id == kLegacyBrowserSupportExtensionId) {
603        // TODO(joaodasilva): remove this special treatment for LBS by M35.
604        std::string schema_json;
605        const base::Value* value = component->second->GetValue(kKeySchema);
606        if (value && value->GetAsString(&schema_json)) {
607          std::string error;
608          schema = Schema::Parse(PatchSchema(schema_json), &error);
609          if (!schema.valid())
610            LOG(WARNING) << "Invalid schema in the registry for LBS: " << error;
611        }
612      }
613
614      // Parse policy.
615      for (size_t j = 0; j < arraysize(kLevels); j++) {
616        const RegistryDict* policy_dict =
617            component->second->GetKey(kLevels[j].path);
618        if (!policy_dict)
619          continue;
620
621        PolicyMap policy;
622        ParsePolicy(policy_dict, kLevels[j].level, scope, schema, &policy);
623        bundle->Get(policy_namespace).MergeFrom(policy);
624      }
625    }
626  }
627}
628
629void PolicyLoaderWin::SetupWatches() {
630  DCHECK(is_initialized_);
631  if (!user_policy_watcher_failed_ &&
632      !user_policy_watcher_.GetWatchedObject() &&
633      !user_policy_watcher_.StartWatching(
634          user_policy_changed_event_.handle(), this)) {
635    DLOG(WARNING) << "Failed to start watch for user policy change event";
636    user_policy_watcher_failed_ = true;
637  }
638  if (!machine_policy_watcher_failed_ &&
639      !machine_policy_watcher_.GetWatchedObject() &&
640      !machine_policy_watcher_.StartWatching(
641          machine_policy_changed_event_.handle(), this)) {
642    DLOG(WARNING) << "Failed to start watch for machine policy change event";
643    machine_policy_watcher_failed_ = true;
644  }
645}
646
647void PolicyLoaderWin::OnObjectSignaled(HANDLE object) {
648  DCHECK(object == user_policy_changed_event_.handle() ||
649         object == machine_policy_changed_event_.handle())
650      << "unexpected object signaled policy reload, obj = "
651      << std::showbase << std::hex << object;
652  Reload(false);
653}
654
655}  // namespace policy
656