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