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