summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy/policy_loader_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/policy/policy_loader_win.cc')
-rw-r--r--chrome/browser/policy/policy_loader_win.cc313
1 files changed, 313 insertions, 0 deletions
diff --git a/chrome/browser/policy/policy_loader_win.cc b/chrome/browser/policy/policy_loader_win.cc
new file mode 100644
index 0000000..c1f1351
--- /dev/null
+++ b/chrome/browser/policy/policy_loader_win.cc
@@ -0,0 +1,313 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/policy_loader_win.h"
+
+#include <string>
+
+#include <string.h>
+
+#include <userenv.h>
+
+// userenv.dll is required for RegisterGPNotification().
+#pragma comment(lib, "userenv.lib")
+
+#include "base/basictypes.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/win/registry.h"
+#include "chrome/browser/policy/policy_bundle.h"
+#include "chrome/browser/policy/policy_map.h"
+#include "policy/policy_constants.h"
+
+using base::win::RegKey;
+
+namespace policy {
+
+namespace {
+
+// Determines the registry key with the highest priority that contains
+// the |key_path| key with a |value_name| value inside.
+// |key_path| is a suffix to the Chrome mandatory or recommended registry key,
+// and can be empty to lookup values directly at that key.
+// |value_name| is the name of a value that should exist at that path. If it
+// is empty, then only the existence of the path is verified.
+// Returns true if |key| was updated to point to a key with a |key_path| suffix
+// and optional |value_name| value inside. In that case, |level| and |scope|
+// will contain the appropriate values for the key found.
+// Returns false otherwise.
+bool LoadHighestPriorityKey(const string16& key_path,
+ const string16& value_name,
+ RegKey* key,
+ PolicyLevel* level,
+ PolicyScope* scope) {
+ // |path| is in decreasing order of priority.
+ static const struct {
+ const wchar_t* path;
+ PolicyLevel level;
+ } key_paths[] = {
+ { kRegistryMandatorySubKey, POLICY_LEVEL_MANDATORY },
+ { kRegistryRecommendedSubKey, POLICY_LEVEL_RECOMMENDED },
+ };
+
+ // |hive| is in decreasing order of priority.
+ static const struct {
+ HKEY hive;
+ PolicyScope scope;
+ } hives[] = {
+ { HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE },
+ { HKEY_CURRENT_USER, POLICY_SCOPE_USER },
+ };
+
+ // Lookup at the mandatory path for both user and machine policies first, and
+ // then at the recommended path.
+ for (size_t k = 0; k < arraysize(key_paths); ++k) {
+ for (size_t h = 0; h < arraysize(hives); ++h) {
+ string16 path(key_paths[k].path);
+ if (!key_path.empty())
+ path += ASCIIToUTF16("\\") + key_path;
+ key->Open(hives[h].hive, path.c_str(), KEY_READ);
+ if (!key->Valid())
+ continue;
+ if (value_name.empty() || key->HasValue(value_name.c_str())) {
+ *level = key_paths[k].level;
+ *scope = hives[h].scope;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Reads a REG_SZ string at |key| named |name| into |result|. Returns false if
+// the string could not be read.
+bool ReadRegistryString(RegKey* key,
+ const string16& name,
+ string16* result) {
+ DWORD value_size = 0;
+ DWORD key_type = 0;
+ scoped_array<uint8> buffer;
+
+ if (key->ReadValue(name.c_str(), 0, &value_size, &key_type) != ERROR_SUCCESS)
+ return false;
+ if (key_type != REG_SZ)
+ return false;
+
+ // According to the Microsoft documentation, the string
+ // buffer may not be explicitly 0-terminated. Allocate a
+ // slightly larger buffer and pre-fill to zeros to guarantee
+ // the 0-termination.
+ buffer.reset(new uint8[value_size + 2]);
+ memset(buffer.get(), 0, value_size + 2);
+ key->ReadValue(name.c_str(), buffer.get(), &value_size, NULL);
+ result->assign(reinterpret_cast<const wchar_t*>(buffer.get()));
+ return true;
+}
+
+// Reads a REG_DWORD integer at |key| named |name| into |result|. Returns false
+// if the value could no be read.
+bool ReadRegistryInteger(RegKey* key,
+ const string16& name,
+ uint32* result) {
+ DWORD dword;
+ if (key->ReadValueDW(name.c_str(), &dword) != ERROR_SUCCESS)
+ return false;
+ *result = dword;
+ return true;
+}
+
+// Returns the Value for a Chrome string policy named |name|, or NULL if
+// it wasn't found. The caller owns the returned value.
+base::Value* ReadStringValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
+ RegKey key;
+ if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
+ return NULL;
+ string16 value;
+ if (!ReadRegistryString(&key, name, &value))
+ return NULL;
+ return base::Value::CreateStringValue(value);
+}
+
+// Returns the Value for a Chrome string list policy named |name|,
+// or NULL if it wasn't found. The caller owns the returned value.
+base::Value* ReadStringListValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
+ RegKey key;
+ if (!LoadHighestPriorityKey(name, string16(), &key, level, scope))
+ return NULL;
+ base::ListValue* result = new base::ListValue();
+ string16 value;
+ int index = 0;
+ while (ReadRegistryString(&key, base::IntToString16(++index), &value))
+ result->Append(base::Value::CreateStringValue(value));
+ return result;
+}
+
+// Returns the Value for a Chrome boolean policy named |name|,
+// or NULL if it wasn't found. The caller owns the returned value.
+base::Value* ReadBooleanValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
+ RegKey key;
+ if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
+ return NULL;
+ uint32 value;
+ if (!ReadRegistryInteger(&key, name, &value))
+ return NULL;
+ return base::Value::CreateBooleanValue(value != 0u);
+}
+
+// Returns the Value for a Chrome integer policy named |name|,
+// or NULL if it wasn't found. The caller owns the returned value.
+base::Value* ReadIntegerValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
+ RegKey key;
+ if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
+ return NULL;
+ uint32 value;
+ if (!ReadRegistryInteger(&key, name, &value))
+ return NULL;
+ return base::Value::CreateIntegerValue(value);
+}
+
+// Returns the Value for a Chrome dictionary policy named |name|,
+// or NULL if it wasn't found. The caller owns the returned value.
+base::Value* ReadDictionaryValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
+ // Dictionaries are encoded as JSON strings on Windows.
+ //
+ // A dictionary could be stored as a subkey, with each of its entries
+ // within that subkey. However, it would be impossible to recover the
+ // type for some of those entries:
+ // - Booleans are stored as DWORDS and are indistinguishable from
+ // integers;
+ // - Lists are stored as a subkey, with entries mapping 0 to N-1 to
+ // their value. This is indistinguishable from a Dictionary with
+ // integer keys.
+ //
+ // The GPO policy editor also has a limited data entry form that doesn't
+ // support dictionaries.
+ RegKey key;
+ if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
+ return NULL;
+ string16 value;
+ if (!ReadRegistryString(&key, name, &value))
+ return NULL;
+ return base::JSONReader::Read(UTF16ToUTF8(value));
+}
+
+} // namespace
+
+PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list)
+ : is_initialized_(false),
+ policy_list_(policy_list),
+ user_policy_changed_event_(false, false),
+ machine_policy_changed_event_(false, false),
+ user_policy_watcher_failed_(false),
+ machine_policy_watcher_failed_(false) {
+ if (!RegisterGPNotification(user_policy_changed_event_.handle(), false)) {
+ DPLOG(WARNING) << "Failed to register user group policy notification";
+ user_policy_watcher_failed_ = true;
+ }
+ if (!RegisterGPNotification(machine_policy_changed_event_.handle(), true)) {
+ DPLOG(WARNING) << "Failed to register machine group policy notification.";
+ machine_policy_watcher_failed_ = true;
+ }
+}
+
+PolicyLoaderWin::~PolicyLoaderWin() {
+ user_policy_watcher_.StopWatching();
+ machine_policy_watcher_.StopWatching();
+}
+
+void PolicyLoaderWin::InitOnFile() {
+ is_initialized_ = true;
+ SetupWatches();
+}
+
+scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() {
+ // Reset the watches BEFORE reading the individual policies to avoid
+ // missing a change notification.
+ if (is_initialized_)
+ SetupWatches();
+
+ scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
+ PolicyMap& chrome_policy = bundle->Get(POLICY_DOMAIN_CHROME, std::string());
+
+ const PolicyDefinitionList::Entry* current;
+ for (current = policy_list_->begin; current != policy_list_->end; ++current) {
+ const string16 name(ASCIIToUTF16(current->name));
+ PolicyLevel level = POLICY_LEVEL_MANDATORY;
+ PolicyScope scope = POLICY_SCOPE_MACHINE;
+ Value* value = NULL;
+
+ switch (current->value_type) {
+ case Value::TYPE_STRING:
+ value = ReadStringValue(name, &level, &scope);
+ break;
+
+ case Value::TYPE_LIST:
+ value = ReadStringListValue(name, &level, &scope);
+ break;
+
+ case Value::TYPE_BOOLEAN:
+ value = ReadBooleanValue(name, &level, &scope);
+ break;
+
+ case Value::TYPE_INTEGER:
+ value = ReadIntegerValue(name, &level, &scope);
+ break;
+
+ case Value::TYPE_DICTIONARY:
+ value = ReadDictionaryValue(name, &level, &scope);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+
+ if (value)
+ chrome_policy.Set(current->name, level, scope, value);
+ }
+
+ return bundle.Pass();
+}
+
+void PolicyLoaderWin::SetupWatches() {
+ DCHECK(is_initialized_);
+ if (!user_policy_watcher_failed_ &&
+ !user_policy_watcher_.GetWatchedObject() &&
+ !user_policy_watcher_.StartWatching(
+ user_policy_changed_event_.handle(), this)) {
+ DLOG(WARNING) << "Failed to start watch for user policy change event";
+ user_policy_watcher_failed_ = true;
+ }
+ if (!machine_policy_watcher_failed_ &&
+ !machine_policy_watcher_.GetWatchedObject() &&
+ !machine_policy_watcher_.StartWatching(
+ machine_policy_changed_event_.handle(), this)) {
+ DLOG(WARNING) << "Failed to start watch for machine policy change event";
+ machine_policy_watcher_failed_ = true;
+ }
+}
+
+void PolicyLoaderWin::OnObjectSignaled(HANDLE object) {
+ DCHECK(object == user_policy_changed_event_.handle() ||
+ object == machine_policy_changed_event_.handle())
+ << "unexpected object signaled policy reload, obj = "
+ << std::showbase << std::hex << object;
+ Reload(false);
+}
+
+} // namespace policy