Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(981)

Side by Side Diff: chrome/browser/policy/policy_loader_win.cc

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

Powered by Google App Engine
This is Rietveld 408576698