• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
PatchSchema(const std::string & schema)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.
FilterUntrustedPolicy(PolicyMap * policy)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:
Wow64Functions()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 
is_valid()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 
IsWow64()222   bool IsWow64() {
223     BOOL result = 0;
224     if (!is_wow_64_process_(GetCurrentProcess(), &result))
225       PLOG(WARNING) << "IsWow64ProcFailed";
226     return !!result;
227   }
228 
DisableFsRedirection(PVOID * previous_state)229   bool DisableFsRedirection(PVOID* previous_state) {
230     return !!wow_64_disable_wow_64_fs_redirection_(previous_state);
231   }
232 
RevertFsRedirection(PVOID previous_state)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:
ScopedDisableWow64Redirection()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 
~ScopedDisableWow64Redirection()270   ~ScopedDisableWow64Redirection() {
271     if (active_)
272       CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_));
273   }
274 
is_active()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:
~WinGPOListProvider()287   virtual ~WinGPOListProvider() {}
288 
289   // AppliedGPOListProvider:
GetAppliedGPOList(DWORD flags,LPCTSTR machine_name,PSID sid_user,GUID * extension_guid,PGROUP_POLICY_OBJECT * gpo_list)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 
FreeGPOList(PGROUP_POLICY_OBJECT gpo_list)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|.
ParsePolicy(const RegistryDict * gpo_dict,PolicyLevel level,PolicyScope scope,const Schema & schema,PolicyMap * policy)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.
CollectEnterpriseUMAs()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 
PolicyLoaderWin(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::string16 & chrome_policy_key,AppliedGPOListProvider * gpo_provider)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 
~PolicyLoaderWin()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
Create(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::string16 & chrome_policy_key)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 
InitOnBackgroundThread()412 void PolicyLoaderWin::InitOnBackgroundThread() {
413   is_initialized_ = true;
414   SetupWatches();
415   CollectEnterpriseUMAs();
416 }
417 
Load()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 
ReadPRegFile(const base::FilePath & preg_file,RegistryDict * policy,PolicyLoadStatusSample * status)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 
LoadGPOPolicy(PolicyScope scope,PGROUP_POLICY_OBJECT policy_object_list,RegistryDict * policy,PolicyLoadStatusSample * status)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 
ReadPolicyFromGPO(PolicyScope scope,RegistryDict * policy,PolicyLoadStatusSample * status)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 
LoadChromePolicy(const RegistryDict * gpo_dict,PolicyLevel level,PolicyScope scope,PolicyMap * chrome_policy_map)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 
Load3rdPartyPolicy(const RegistryDict * gpo_dict,PolicyScope scope,PolicyBundle * bundle)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 
SetupWatches()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 
OnObjectSignaled(HANDLE object)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