summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-12 17:55:13 +0000
committermnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-12 17:55:13 +0000
commite54c7925e3f99aa2a618c9e5e4bcb396ef70bd33 (patch)
tree6fa622d24edad2618f57f70561acd4642bb1196c
parentcdac5151b6c8590f74e797cd58161ef67017958f (diff)
downloadchromium_src-e54c7925e3f99aa2a618c9e5e4bcb396ef70bd33.zip
chromium_src-e54c7925e3f99aa2a618c9e5e4bcb396ef70bd33.tar.gz
chromium_src-e54c7925e3f99aa2a618c9e5e4bcb396ef70bd33.tar.bz2
Change PolicyLoaderWin to load PReg files if possible.
We now query whether GPO is present and read directly from the corresponding PReg files. The registry is used only as a fallback source for policy values. To accomplish this, PolicyLoaderWin now works in two steps: It first converts the GPO input into base::Value representation and then maps that to proper policy values. This also unifies the handling for Chrome policy and 3rd-party policy. BUG= TEST=unit tests TBR=grt@chromium.org, cpu@chromium.org, ben@chromium.org Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=193649 Review URL: https://codereview.chromium.org/13619014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@193961 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/breakpad_win.cc10
-rw-r--r--chrome/browser/policy/browser_policy_connector.cc2
-rw-r--r--chrome/browser/policy/policy_loader_win.cc970
-rw-r--r--chrome/browser/policy/policy_loader_win.h88
-rw-r--r--chrome/browser/policy/policy_loader_win_unittest.cc265
-rw-r--r--chrome/browser/policy/policy_path_parser_win.cc2
-rwxr-xr-xchrome/tools/build/generate_policy_source.py25
-rw-r--r--chrome_frame/chrome_launcher.cc2
-rw-r--r--chrome_frame/policy_settings.cc6
-rw-r--r--chrome_frame/test/policy_settings_unittest.cc6
10 files changed, 904 insertions, 472 deletions
diff --git a/chrome/app/breakpad_win.cc b/chrome/app/breakpad_win.cc
index 8296eba..681dd88 100644
--- a/chrome/app/breakpad_win.cc
+++ b/chrome/app/breakpad_win.cc
@@ -4,10 +4,10 @@
#include "chrome/app/breakpad_win.h"
-#include <windows.h>
#include <shellapi.h>
#include <tchar.h>
#include <userenv.h>
+#include <windows.h>
#include <algorithm>
#include <vector>
@@ -19,8 +19,8 @@
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/memory/scoped_ptr.h"
-#include "base/string_util.h"
#include "base/string16.h"
+#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/strings/string_split.h"
#include "base/utf_string_conversions.h"
@@ -57,9 +57,9 @@ bool g_deferred_crash_uploads = false;
} // namespace breakpad_win
using breakpad_win::g_custom_entries;
+using breakpad_win::g_deferred_crash_uploads;
using breakpad_win::g_experiment_chunks_offset;
using breakpad_win::g_num_of_experiments_offset;
-using breakpad_win::g_deferred_crash_uploads;
namespace {
@@ -288,14 +288,14 @@ static bool MetricsReportingControlledByPolicy(bool* result) {
string16 key_name = UTF8ToUTF16(policy::key::kMetricsReportingEnabled);
DWORD value = 0;
base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE,
- policy::kRegistryMandatorySubKey, KEY_READ);
+ policy::kRegistryChromePolicyKey, KEY_READ);
if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
*result = value != 0;
return true;
}
base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER,
- policy::kRegistryMandatorySubKey, KEY_READ);
+ policy::kRegistryChromePolicyKey, KEY_READ);
if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
*result = value != 0;
return true;
diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc
index 45cf305..cafe4c5 100644
--- a/chrome/browser/policy/browser_policy_connector.cc
+++ b/chrome/browser/policy/browser_policy_connector.cc
@@ -567,7 +567,7 @@ scoped_ptr<PolicyService>
ConfigurationPolicyProvider* BrowserPolicyConnector::CreatePlatformProvider() {
#if defined(OS_WIN)
const PolicyDefinitionList* policy_list = GetChromePolicyDefinitionList();
- scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin(policy_list));
+ scoped_ptr<AsyncPolicyLoader> loader(PolicyLoaderWin::Create(policy_list));
return new AsyncPolicyProvider(loader.Pass());
#elif defined(OS_MACOSX)
const PolicyDefinitionList* policy_list = GetChromePolicyDefinitionList();
diff --git a/chrome/browser/policy/policy_loader_win.cc b/chrome/browser/policy/policy_loader_win.cc
index b527308..2abbeeb 100644
--- a/chrome/browser/policy/policy_loader_win.cc
+++ b/chrome/browser/policy/policy_loader_win.cc
@@ -5,25 +5,35 @@
#include "chrome/browser/policy/policy_loader_win.h"
#include <string>
+#include <vector>
-#include <string.h>
+#include <rpc.h> // For struct GUID
+#include <shlwapi.h> // For PathIsUNC()
+#include <userenv.h> // For GPO functions
+#include <windows.h>
-#include <userenv.h>
-
-// userenv.dll is required for RegisterGPNotification().
+// shlwapi.dll is required for PathIsUNC().
+#pragma comment(lib, "shlwapi.lib")
+// userenv.dll is required for various GPO functions.
#pragma comment(lib, "userenv.lib")
#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
#include "base/json/json_reader.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
+#include "base/scoped_native_library.h"
+#include "base/stl_util.h"
#include "base/string16.h"
+#include "base/string_util.h"
#include "base/strings/string_number_conversions.h"
+#include "base/sys_byteorder.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 "chrome/browser/policy/preg_parser_win.h"
#include "chrome/common/json_schema/json_schema_constants.h"
#include "policy/policy_constants.h"
@@ -32,70 +42,183 @@ namespace schema = json_schema_constants;
using base::win::RegKey;
using base::win::RegistryKeyIterator;
using base::win::RegistryValueIterator;
-using namespace policy::registry_constants;
namespace policy {
-namespace registry_constants {
- const wchar_t kPathSep[] = L"\\";
- const wchar_t kThirdParty[] = L"3rdparty";
- const wchar_t kMandatory[] = L"policy";
- const wchar_t kRecommended[] = L"recommended";
- const wchar_t kSchema[] = L"schema";
-} // namespace registry_constants
-
namespace {
-// Map of registry hives to their corresponding policy scope, in decreasing
-// order of priority.
-const struct {
- HKEY hive;
- PolicyScope scope;
-} kHives[] = {
- { HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE },
- { HKEY_CURRENT_USER, POLICY_SCOPE_USER },
+const char kKeyMandatory[] = "policy";
+const char kKeyRecommended[] = "recommended";
+const char kKeySchema[] = "schema";
+const char kKeyThirdParty[] = "3rdparty";
+
+// The GUID of the registry settings group policy extension.
+GUID kRegistrySettingsCSEGUID = REGISTRY_EXTENSION_GUID;
+
+// The PReg file name.
+const base::FilePath::CharType kPRegFileName[] =
+ FILE_PATH_LITERAL("Registry.pol");
+
+// A helper class encapsulating run-time-linked function calls to Wow64 APIs.
+class Wow64Functions {
+ public:
+ Wow64Functions()
+ : kernel32_lib_(base::FilePath(L"kernel32")),
+ is_wow_64_process_(NULL),
+ wow_64_disable_wow_64_fs_redirection_(NULL),
+ wow_64_revert_wow_64_fs_redirection_(NULL) {
+ if (kernel32_lib_.is_valid()) {
+ is_wow_64_process_ = static_cast<IsWow64Process>(
+ kernel32_lib_.GetFunctionPointer("IsWow64Process"));
+ wow_64_disable_wow_64_fs_redirection_ =
+ static_cast<Wow64DisableWow64FSRedirection>(
+ kernel32_lib_.GetFunctionPointer(
+ "Wow64DisableWow64FsRedirection"));
+ wow_64_revert_wow_64_fs_redirection_ =
+ static_cast<Wow64RevertWow64FSRedirection>(
+ kernel32_lib_.GetFunctionPointer(
+ "Wow64RevertWow64FsRedirection"));
+ }
+ }
+
+ bool is_valid() {
+ return is_wow_64_process_ &&
+ wow_64_disable_wow_64_fs_redirection_ &&
+ wow_64_revert_wow_64_fs_redirection_;
+ }
+
+ bool IsWow64() {
+ BOOL result = 0;
+ if (!is_wow_64_process_(GetCurrentProcess(), &result))
+ PLOG(WARNING) << "IsWow64ProcFailed";
+ return !!result;
+ }
+
+ bool DisableFsRedirection(PVOID* previous_state) {
+ return !!wow_64_disable_wow_64_fs_redirection_(previous_state);
+ }
+
+ bool RevertFsRedirection(PVOID previous_state) {
+ return !!wow_64_revert_wow_64_fs_redirection_(previous_state);
+ }
+
+ private:
+ typedef BOOL (WINAPI* IsWow64Process)(HANDLE, PBOOL);
+ typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*);
+ typedef BOOL (WINAPI* Wow64RevertWow64FSRedirection)(PVOID);
+
+ base::ScopedNativeLibrary kernel32_lib_;
+
+ IsWow64Process is_wow_64_process_;
+ Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection_;
+ Wow64RevertWow64FSRedirection wow_64_revert_wow_64_fs_redirection_;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64Functions);
};
-// 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;
+// Global Wow64Function instance used by ScopedDisableWow64Redirection below.
+static base::LazyInstance<Wow64Functions> g_wow_64_functions =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Scoper that switches off Wow64 File System Redirection during its lifetime.
+class ScopedDisableWow64Redirection {
+ public:
+ ScopedDisableWow64Redirection()
+ : active_(false),
+ previous_state_(NULL) {
+ Wow64Functions* wow64 = g_wow_64_functions.Pointer();
+ if (wow64->is_valid() && wow64->IsWow64()) {
+ if (wow64->DisableFsRedirection(&previous_state_))
+ active_ = true;
+ else
+ PLOG(WARNING) << "Wow64DisableWow64FSRedirection";
+ }
+ }
- if (key->ReadValue(name.c_str(), 0, &value_size, &key_type) != ERROR_SUCCESS)
- return false;
- if (key_type != REG_SZ)
- return false;
+ ~ScopedDisableWow64Redirection() {
+ if (active_)
+ CHECK(g_wow_64_functions.Get().RevertFsRedirection(previous_state_));
+ }
- // 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;
+ bool is_active() { return active_; }
+
+ private:
+ bool active_;
+ PVOID previous_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableWow64Redirection);
+};
+
+// AppliedGPOListProvider implementation that calls actual Windows APIs.
+class WinGPOListProvider : public AppliedGPOListProvider {
+ public:
+ virtual ~WinGPOListProvider() {}
+
+ // AppliedGPOListProvider:
+ virtual DWORD GetAppliedGPOList(DWORD flags,
+ LPCTSTR machine_name,
+ PSID sid_user,
+ GUID* extension_guid,
+ PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE {
+ return ::GetAppliedGPOList(flags, machine_name, sid_user, extension_guid,
+ gpo_list);
+ }
+
+ virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE {
+ return ::FreeGPOList(gpo_list);
+ }
+};
+
+// The default windows GPO list provider used for PolicyLoaderWin.
+static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
+const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
+ const std::string& name) {
+ if (!dictionary)
+ return NULL;
+ const base::DictionaryValue* entry = NULL;
+ dictionary->GetDictionaryWithoutPathExpansion(name, &entry);
+ return entry;
}
-// 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;
+// Tries to extract the dictionary at |key| in |dict| and returns it.
+scoped_ptr<base::DictionaryValue> RemoveDict(base::DictionaryValue* dict,
+ const std::string& key) {
+ base::Value* entry = NULL;
+ base::DictionaryValue* result_dict = NULL;
+ if (dict && dict->RemoveWithoutPathExpansion(key, &entry) && entry) {
+ if (!entry->GetAsDictionary(&result_dict))
+ delete entry;
+ }
+
+ return make_scoped_ptr(result_dict);
+}
+
+std::string GetSchemaTypeForValueType(base::Value::Type value_type) {
+ switch (value_type) {
+ case base::Value::TYPE_DICTIONARY:
+ return json_schema_constants::kObject;
+ case base::Value::TYPE_INTEGER:
+ return json_schema_constants::kInteger;
+ case base::Value::TYPE_LIST:
+ return json_schema_constants::kArray;
+ case base::Value::TYPE_BOOLEAN:
+ return json_schema_constants::kBoolean;
+ case base::Value::TYPE_STRING:
+ return json_schema_constants::kString;
+ default:
+ break;
+ }
+
+ NOTREACHED() << "Unsupported policy value type " << value_type;
+ return json_schema_constants::kNull;
}
// Returns the Value type described in |schema|, or |default_type| if not found.
-base::Value::Type GetType(const base::DictionaryValue* schema,
- base::Value::Type default_type) {
+base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema,
+ base::Value::Type default_type) {
// JSON-schema types to base::Value::Type mapping.
static const struct {
// JSON schema type.
@@ -115,7 +238,7 @@ base::Value::Type GetType(const base::DictionaryValue* schema,
if (!schema)
return default_type;
std::string type;
- if (!schema->GetString(schema::kType, &type))
+ if (!schema->GetStringWithoutPathExpansion(schema::kType, &type))
return default_type;
for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
if (type == kSchemaToValueTypeMap[i].schema_type)
@@ -124,28 +247,11 @@ base::Value::Type GetType(const base::DictionaryValue* schema,
return default_type;
}
-// Returns the default type for registry entries of |reg_type|, when there is
-// no schema defined type for a policy.
-base::Value::Type GetDefaultFor(DWORD reg_type) {
- return reg_type == REG_DWORD ? base::Value::TYPE_INTEGER :
- base::Value::TYPE_STRING;
-}
-
-// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
-const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
- const std::string& name) {
- if (!dictionary)
- return NULL;
- const base::DictionaryValue* entry = NULL;
- dictionary->GetDictionary(name, &entry);
- return entry;
-}
-
// Returns the schema for property |name| given the |schema| of an object.
// Returns the "additionalProperties" schema if no specific schema for
// |name| is present. Returns NULL if no schema is found.
const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
- const std::string& name) {
+ const std::string& name) {
const base::DictionaryValue* properties =
GetEntry(schema, schema::kProperties);
const base::DictionaryValue* sub_schema = GetEntry(properties, name);
@@ -155,241 +261,201 @@ const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
return GetEntry(schema, schema::kAdditionalProperties);
}
-// Converts string |value| to another |type|, if possible.
-base::Value* ConvertStringValue(const string16& value, base::Value::Type type) {
- switch (type) {
- case base::Value::TYPE_NULL:
- return base::Value::CreateNullValue();
-
- case base::Value::TYPE_BOOLEAN: {
- int int_value;
- if (base::StringToInt(value, &int_value))
- return base::Value::CreateBooleanValue(int_value != 0);
- return NULL;
+// Reads the subtree of the Windows registry at |root| into the passed |dict|.
+void ReadRegistry(HKEY hive,
+ const string16& root,
+ base::DictionaryValue* dict) {
+ // First, read all the values of the key.
+ for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
+ const std::string name = UTF16ToUTF8(it.Name());
+ switch (it.Type()) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ dict->SetStringWithoutPathExpansion(name, UTF16ToUTF8(it.Value()));
+ continue;
+ case REG_DWORD_LITTLE_ENDIAN:
+ case REG_DWORD_BIG_ENDIAN:
+ if (it.ValueSize() == sizeof(DWORD)) {
+ DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
+ if (it.Type() == REG_DWORD_BIG_ENDIAN)
+ dword_value = base::NetToHost32(dword_value);
+ else
+ dword_value = base::ByteSwapToLE32(dword_value);
+ dict->SetIntegerWithoutPathExpansion(name, dword_value);
+ continue;
+ }
+ case REG_NONE:
+ case REG_LINK:
+ case REG_MULTI_SZ:
+ case REG_RESOURCE_LIST:
+ case REG_FULL_RESOURCE_DESCRIPTOR:
+ case REG_RESOURCE_REQUIREMENTS_LIST:
+ case REG_QWORD_LITTLE_ENDIAN:
+ // Unsupported type, message gets logged below.
+ break;
}
- case base::Value::TYPE_INTEGER: {
- int int_value;
- if (base::StringToInt(value, &int_value))
- return base::Value::CreateIntegerValue(int_value);
- return NULL;
- }
+ LOG(WARNING) << "Failed to read hive " << hive << " at "
+ << root << "\\" << name
+ << " type " << it.Type();
+ }
- case base::Value::TYPE_DOUBLE: {
- double double_value;
- if (base::StringToDouble(UTF16ToUTF8(value), &double_value))
- return base::Value::CreateDoubleValue(double_value);
- DLOG(WARNING) << "Failed to read policy value as double: " << value;
- return NULL;
+ // Recurse for all subkeys.
+ for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
+ std::string name(UTF16ToUTF8(it.Name()));
+ if (dict->HasKey(name)) {
+ DLOG(WARNING) << "Ignoring registry key because a value exists with the "
+ "same name: " << root << "\\" << name;
+ } else {
+ scoped_ptr<base::DictionaryValue> subdict(new base::DictionaryValue());
+ ReadRegistry(hive, root + L"\\" + it.Name(), subdict.get());
+ dict->SetWithoutPathExpansion(name, subdict.release());
}
-
- case base::Value::TYPE_STRING:
- return base::Value::CreateStringValue(value);
-
- case base::Value::TYPE_DICTIONARY:
- case base::Value::TYPE_LIST:
- return base::JSONReader::Read(UTF16ToUTF8(value));
-
- case base::Value::TYPE_BINARY:
- DLOG(WARNING) << "Cannot convert REG_SZ entry to type " << type;
- return NULL;
}
- NOTREACHED();
- return NULL;
}
-// Converts an integer |value| to another |type|, if possible.
-base::Value* ConvertIntegerValue(uint32 value, base::Value::Type type) {
- switch (type) {
- case base::Value::TYPE_BOOLEAN:
- return base::Value::CreateBooleanValue(value != 0);
-
- case base::Value::TYPE_INTEGER:
- return base::Value::CreateIntegerValue(value);
-
- case base::Value::TYPE_DOUBLE:
- return base::Value::CreateDoubleValue(value);
-
- case base::Value::TYPE_NULL:
- case base::Value::TYPE_STRING:
- case base::Value::TYPE_BINARY:
- case base::Value::TYPE_DICTIONARY:
- case base::Value::TYPE_LIST:
- DLOG(WARNING) << "Cannot convert REG_DWORD entry to type " << type;
- return NULL;
+// Converts |value| in raw GPO representation to the internal policy value, as
+// described by |schema|. This maps the ambiguous GPO data types to the
+// internal policy value representations.
+scoped_ptr<base::Value> ConvertPolicyValue(
+ const base::Value& value,
+ const base::DictionaryValue* schema) {
+ // Figure out the type to convert to from the schema.
+ const base::Value::Type result_type(
+ GetValueTypeForSchema(schema, value.GetType()));
+
+ // If the type is good already, go with it.
+ if (value.IsType(result_type)) {
+ // Recurse for complex types if there is a schema.
+ if (schema) {
+ const base::DictionaryValue* dict = NULL;
+ const base::ListValue* list = NULL;
+ if (value.GetAsDictionary(&dict)) {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
+ entry.Advance()) {
+ scoped_ptr<base::Value> converted_value(
+ ConvertPolicyValue(entry.value(),
+ GetSchemaFor(schema, entry.key())));
+ result->SetWithoutPathExpansion(entry.key(),
+ converted_value.release());
+ }
+ return result.Pass();
+ } else if (value.GetAsList(&list)) {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ const base::DictionaryValue* item_schema =
+ GetEntry(schema, schema::kItems);
+ for (base::ListValue::const_iterator entry(list->begin());
+ entry != list->end(); ++entry) {
+ result->Append(ConvertPolicyValue(**entry, item_schema).release());
+ }
+ return result.Pass();
+ }
+ }
+ return make_scoped_ptr(value.DeepCopy());
}
- NOTREACHED();
- return NULL;
-}
-// Reads a value from the registry |key| named |name| with registry type
-// |registry_type| as a value of type |type|.
-// Returns NULL if the value could not be loaded or converted.
-base::Value* ReadPolicyValue(RegKey* key,
- const string16& name,
- DWORD registry_type,
- base::Value::Type type) {
- switch (registry_type) {
- case REG_SZ: {
- string16 value;
- if (ReadRegistryString(key, name, &value))
- return ConvertStringValue(value, type);
- break;
+ // Else, do some conversions to map windows registry data types to JSON types.
+ std::string string_value;
+ int int_value = 0;
+ switch (result_type) {
+ case base::Value::TYPE_NULL: {
+ return make_scoped_ptr(base::Value::CreateNullValue());
}
-
- case REG_DWORD: {
- uint32 value;
- if (ReadRegistryInteger(key, name, &value))
- return ConvertIntegerValue(value, type);
+ case base::Value::TYPE_BOOLEAN: {
+ // Accept booleans encoded as either string or integer.
+ if (value.GetAsInteger(&int_value) ||
+ (value.GetAsString(&string_value) &&
+ base::StringToInt(string_value, &int_value))) {
+ return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0));
+ }
break;
}
-
- default:
- DLOG(WARNING) << "Registry type not supported for key " << name;
+ case base::Value::TYPE_INTEGER: {
+ // Integers may be string-encoded.
+ if (value.GetAsString(&string_value) &&
+ base::StringToInt(string_value, &int_value)) {
+ return make_scoped_ptr(base::Value::CreateIntegerValue(int_value));
+ }
break;
- }
- return NULL;
-}
-
-// Forward declaration for ReadComponentListValue().
-base::DictionaryValue* ReadComponentDictionaryValue(
- HKEY hive,
- const string16& path,
- const base::DictionaryValue* schema);
-
-// Loads the list at |path| in the given |hive|. |schema| is a JSON schema
-// (http://json-schema.org/) that describes the expected type of the list.
-// Ownership of the result is transferred to the caller.
-base::ListValue* ReadComponentListValue(HKEY hive,
- const string16& path,
- const base::DictionaryValue* schema) {
- // The sub-elements are indexed from 1 to N. They can be represented as
- // registry values or registry keys though; use |schema| first to try to
- // determine the right type, and if that fails default to STRING.
-
- RegKey key(hive, path.c_str(), KEY_READ);
- if (!key.Valid())
- return NULL;
-
- // Get the schema for list items.
- schema = GetEntry(schema, schema::kItems);
- base::Value::Type type = GetType(schema, base::Value::TYPE_STRING);
- base::ListValue* list = new base::ListValue();
- for (int i = 1; ; ++i) {
- string16 name = base::IntToString16(i);
- base::Value* value = NULL;
- if (type == base::Value::TYPE_DICTIONARY) {
- value =
- ReadComponentDictionaryValue(hive, path + kPathSep + name, schema);
- } else if (type == base::Value::TYPE_LIST) {
- value = ReadComponentListValue(hive, path + kPathSep + name, schema);
- } else {
- DWORD reg_type;
- key.ReadValue(name.c_str(), NULL, NULL, &reg_type);
- if (reg_type != REG_NONE)
- value = ReadPolicyValue(&key, name, reg_type, type);
}
- if (value)
- list->Append(value);
- else
+ case base::Value::TYPE_DOUBLE: {
+ // Doubles may be string-encoded or integer-encoded.
+ double double_value = 0;
+ if (value.GetAsInteger(&int_value)) {
+ return make_scoped_ptr(base::Value::CreateDoubleValue(int_value));
+ } else if (value.GetAsString(&string_value) &&
+ base::StringToDouble(string_value, &double_value)) {
+ return make_scoped_ptr(base::Value::CreateDoubleValue(double_value));
+ }
break;
- }
- return list;
-}
-
-// Loads the dictionary at |path| in the given |hive|. |schema| is a JSON
-// schema (http://json-schema.org/) that describes the expected types for the
-// dictionary entries. When the type for a certain entry isn't described in the
-// schema, a default conversion takes place. |schema| can be NULL.
-// Ownership of the result is transferred to the caller.
-base::DictionaryValue* ReadComponentDictionaryValue(
- HKEY hive,
- const string16& path,
- const base::DictionaryValue* schema) {
- // A "value" in the registry is like a file in a filesystem, and a "key" is
- // like a directory, that contains other "values" and "keys".
- // Unfortunately it is possible to have a name both as a "value" and a "key".
- // In those cases, the sub "key" will be ignored; this choice is arbitrary.
-
- // First iterate over all the "values" in |path| and convert them; then
- // recurse into each "key" in |path| and convert them as dictionaries.
-
- RegKey key(hive, path.c_str(), KEY_READ);
- if (!key.Valid())
- return NULL;
-
- base::DictionaryValue* dict = new base::DictionaryValue();
- for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) {
- string16 name16(it.Name());
- std::string name(UTF16ToUTF8(name16));
- const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name);
- base::Value::Type type = GetType(sub_schema, GetDefaultFor(it.Type()));
- base::Value* value = ReadPolicyValue(&key, name16, it.Type(), type);
- if (value)
- dict->Set(name, value);
- }
-
- for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) {
- string16 name16(it.Name());
- std::string name(UTF16ToUTF8(name16));
- if (dict->HasKey(name)) {
- DLOG(WARNING) << "Ignoring registry key because a value exists with the "
- "same name: " << path << kPathSep << name;
- continue;
}
-
- const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name);
- base::Value::Type type = GetType(sub_schema, base::Value::TYPE_DICTIONARY);
- base::Value* value = NULL;
- const string16 sub_path = path + kPathSep + name16;
- if (type == base::Value::TYPE_DICTIONARY) {
- value = ReadComponentDictionaryValue(hive, sub_path, sub_schema);
- } else if (type == base::Value::TYPE_LIST) {
- value = ReadComponentListValue(hive, sub_path, sub_schema);
- } else {
- DLOG(WARNING) << "Can't read a simple type in registry key at " << path;
+ case base::Value::TYPE_LIST: {
+ // Lists are encoded as subkeys with numbered value in the registry.
+ const base::DictionaryValue* dict = NULL;
+ if (value.GetAsDictionary(&dict)) {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ const base::DictionaryValue* item_schema =
+ GetEntry(schema, schema::kItems);
+ for (int i = 1; ; ++i) {
+ const base::Value* entry = NULL;
+ if (!dict->Get(base::IntToString(i), &entry))
+ break;
+ result->Append(ConvertPolicyValue(*entry, item_schema).release());
+ }
+ return result.Pass();
+ }
+ // Fall through in order to accept lists encoded as JSON strings.
}
- if (value)
- dict->Set(name, value);
+ case base::Value::TYPE_DICTIONARY: {
+ // Dictionaries may be encoded as JSON strings.
+ if (value.GetAsString(&string_value)) {
+ scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
+ if (result && result->IsType(result_type))
+ return result.Pass();
+ }
+ break;
+ }
+ case base::Value::TYPE_STRING:
+ case base::Value::TYPE_BINARY:
+ // No conversion possible.
+ break;
}
- return dict;
+ LOG(WARNING) << "Failed to convert " << value.GetType()
+ << " to " << result_type;
+ return make_scoped_ptr(base::Value::CreateNullValue());
}
-// Reads a JSON schema from the given |registry_value|, at the given
-// |registry_key| in |hive|. |registry_value| must be a string (REG_SZ), and
-// is decoded as JSON data. Returns NULL on failure. Ownership is transferred
-// to the caller.
-base::DictionaryValue* ReadRegistrySchema(HKEY hive,
- const string16& registry_key,
- const string16& registry_value) {
- RegKey key(hive, registry_key.c_str(), KEY_READ);
- string16 schema;
- if (!ReadRegistryString(&key, registry_value, &schema))
- return NULL;
- // A JSON schema is represented in JSON too.
- scoped_ptr<base::Value> value(base::JSONReader::Read(UTF16ToUTF8(schema)));
- if (!value.get())
- return NULL;
- base::DictionaryValue* dict = NULL;
- if (!value->GetAsDictionary(&dict))
- return NULL;
- // The top-level entry must be an object, and each of its properties maps
- // a policy name to its schema.
- if (GetType(dict, base::Value::TYPE_DICTIONARY) !=
- base::Value::TYPE_DICTIONARY) {
- DLOG(WARNING) << "schema top-level type isn't \"object\"";
- return NULL;
+// Parses |gpo_dict| according to |schema| and writes the resulting policy
+// settings to |policy| for the given |scope| and |level|.
+void ParsePolicy(const base::DictionaryValue* gpo_dict,
+ PolicyLevel level,
+ PolicyScope scope,
+ const base::DictionaryValue* schema,
+ PolicyMap* policy) {
+ if (!gpo_dict)
+ return;
+
+ scoped_ptr<base::Value> policy_value(ConvertPolicyValue(*gpo_dict, schema));
+ const base::DictionaryValue* policy_dict = NULL;
+ if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) {
+ LOG(WARNING) << "Root policy object is not a dictionary!";
+ return;
}
- value.release();
- return dict;
+
+ policy->LoadFrom(policy_dict, level, scope);
}
} // namespace
-PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list)
+PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list,
+ const string16& chrome_policy_key,
+ AppliedGPOListProvider* gpo_provider)
: is_initialized_(false),
policy_list_(policy_list),
+ chrome_policy_key_(chrome_policy_key),
+ gpo_provider_(gpo_provider),
user_policy_changed_event_(false, false),
machine_policy_changed_event_(false, false),
user_policy_watcher_failed_(false),
@@ -409,155 +475,259 @@ PolicyLoaderWin::~PolicyLoaderWin() {
machine_policy_watcher_.StopWatching();
}
+// static
+scoped_ptr<PolicyLoaderWin> PolicyLoaderWin::Create(
+ const PolicyDefinitionList* policy_list) {
+ return make_scoped_ptr(
+ new PolicyLoaderWin(policy_list, kRegistryChromePolicyKey,
+ g_win_gpo_list_provider.Pointer()));
+}
+
void PolicyLoaderWin::InitOnFile() {
is_initialized_ = true;
SetupWatches();
}
scoped_ptr<PolicyBundle> PolicyLoaderWin::Load() {
- scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
- LoadChromePolicy(
- &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())));
- Load3rdPartyPolicies(bundle.get());
- return bundle.Pass();
-}
-
-void PolicyLoaderWin::LoadChromePolicy(PolicyMap* chrome_policies) {
// Reset the watches BEFORE reading the individual policies to avoid
// missing a change notification.
if (is_initialized_)
SetupWatches();
- // |kKeyPaths| is in decreasing order of priority.
+ if (chrome_policy_schema_.empty())
+ BuildChromePolicySchema();
+
+ // Policy scope and corresponding hive.
static const struct {
- const wchar_t* path;
- PolicyLevel level;
- } kKeyPaths[] = {
- { kRegistryMandatorySubKey, POLICY_LEVEL_MANDATORY },
- { kRegistryRecommendedSubKey, POLICY_LEVEL_RECOMMENDED },
+ PolicyScope scope;
+ HKEY hive;
+ } kScopes[] = {
+ { POLICY_SCOPE_MACHINE, HKEY_LOCAL_MACHINE },
+ { POLICY_SCOPE_USER, HKEY_CURRENT_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(kKeyPaths); ++k) {
- for (size_t h = 0; h < arraysize(kHives); ++h) {
- // Iterate over keys and values at this hive and path.
- HKEY hive = kHives[h].hive;
- string16 path(kKeyPaths[k].path);
- RegKey key;
- if (key.Open(hive, path.c_str(), KEY_READ) != ERROR_SUCCESS ||
- !key.Valid()) {
- continue;
- }
+ // Load policy data for the different scopes/levels and merge them.
+ scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
+ PolicyMap* chrome_policy =
+ &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+ for (size_t i = 0; i < arraysize(kScopes); ++i) {
+ PolicyScope scope = kScopes[i].scope;
+ base::DictionaryValue gpo_dict;
+
+ HANDLE policy_lock =
+ EnterCriticalPolicySection(scope == POLICY_SCOPE_MACHINE);
+ if (policy_lock == NULL)
+ PLOG(ERROR) << "EnterCriticalPolicySection";
+
+ if (!ReadPolicyFromGPO(scope, &gpo_dict)) {
+ VLOG(1) << "Failed to read GPO files for " << scope
+ << " falling back to registry.";
+ ReadRegistry(kScopes[i].hive, chrome_policy_key_, &gpo_dict);
+ }
- // Iterate over values for most policies.
- for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) {
- std::string name(UTF16ToUTF8(it.Name()));
- // Skip if a higher-priority policy value was already inserted, or
- // if this is the default value (empty string).
- if (chrome_policies->Get(name) || name.empty())
- continue;
- // Get the expected policy type, if this is a known policy.
- base::Value::Type type = GetDefaultFor(it.Type());
- for (const PolicyDefinitionList::Entry* e = policy_list_->begin;
- e != policy_list_->end; ++e) {
- if (name == e->name) {
- type = e->value_type;
- break;
- }
- }
- base::Value* value = ReadPolicyValue(&key, it.Name(), it.Type(), type);
- if (!value)
- value = base::Value::CreateNullValue();
- chrome_policies->Set(name, kKeyPaths[k].level, kHives[h].scope, value);
- }
+ if (!LeaveCriticalPolicySection(policy_lock))
+ PLOG(ERROR) << "LeaveCriticalPolicySection";
- // Iterate over keys for policies of type string-list.
- for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) {
- std::string name(UTF16ToUTF8(it.Name()));
- // Skip if a higher-priority policy value was already inserted, or
- // if this is the 3rd party policy subkey.
- const string16 kThirdParty16(kThirdParty);
- if (chrome_policies->Get(name) || it.Name() == kThirdParty16)
- continue;
- string16 list_path = path + kPathSep + it.Name();
- RegKey key;
- if (key.Open(hive, list_path.c_str(), KEY_READ) != ERROR_SUCCESS ||
- !key.Valid()) {
- continue;
- }
- base::ListValue* result = new base::ListValue();
- string16 value;
- int index = 0;
- while (ReadRegistryString(&key, base::IntToString16(++index), &value))
- result->Append(base::Value::CreateStringValue(value));
- chrome_policies->Set(name, kKeyPaths[k].level, kHives[h].scope, result);
- }
+ // Remove special-cased entries from the GPO dictionary.
+ base::DictionaryValue* temp_dict = NULL;
+ scoped_ptr<base::DictionaryValue> recommended_dict(
+ RemoveDict(&gpo_dict, kKeyRecommended));
+ scoped_ptr<base::DictionaryValue> third_party_dict(
+ RemoveDict(&gpo_dict, kKeyThirdParty));
+
+ // Load Chrome policy.
+ LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy);
+ LoadChromePolicy(recommended_dict.get(), POLICY_LEVEL_RECOMMENDED, scope,
+ chrome_policy);
+
+ // Load 3rd-party policy.
+ if (third_party_dict)
+ Load3rdPartyPolicy(third_party_dict.get(), scope, bundle.get());
+ }
+
+ return bundle.Pass();
+}
+
+void PolicyLoaderWin::BuildChromePolicySchema() {
+ scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue());
+ for (const PolicyDefinitionList::Entry* e = policy_list_->begin;
+ e != policy_list_->end; ++e) {
+ const std::string schema_type = GetSchemaTypeForValueType(e->value_type);
+ scoped_ptr<base::DictionaryValue> entry_schema(new base::DictionaryValue());
+ entry_schema->SetStringWithoutPathExpansion(json_schema_constants::kType,
+ schema_type);
+
+ if (e->value_type == base::Value::TYPE_LIST) {
+ scoped_ptr<base::DictionaryValue> items_schema(
+ new base::DictionaryValue());
+ items_schema->SetStringWithoutPathExpansion(
+ json_schema_constants::kType, json_schema_constants::kString);
+ entry_schema->SetWithoutPathExpansion(json_schema_constants::kItems,
+ items_schema.release());
}
+ properties->SetWithoutPathExpansion(e->name, entry_schema.release());
}
+ chrome_policy_schema_.SetStringWithoutPathExpansion(
+ json_schema_constants::kType, json_schema_constants::kObject);
+ chrome_policy_schema_.SetWithoutPathExpansion(
+ json_schema_constants::kProperties, properties.release());
}
-void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) {
- // Each 3rd party namespace can have policies on both HKLM and HKCU. They
- // should be merged, giving priority to HKLM for policies with the same name.
+bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file,
+ base::DictionaryValue* policy) {
+ // The following deals with the minor annoyance that Wow64 FS redirection
+ // might need to be turned off: This is the case if running as a 32-bit
+ // process on a 64-bit system, in which case Wow64 FS redirection redirects
+ // access to the %WINDIR%/System32/GroupPolicy directory to
+ // %WINDIR%/SysWOW64/GroupPolicy, but the file is actually in the
+ // system-native directory.
+ if (file_util::PathExists(preg_file)) {
+ return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy);
+ } else {
+ // Try with redirection switched off.
+ ScopedDisableWow64Redirection redirection_disable;
+ if (redirection_disable.is_active() && file_util::PathExists(preg_file))
+ return preg_parser::ReadFile(preg_file, chrome_policy_key_, policy);
+ }
+
+ // Report the error.
+ LOG(ERROR) << "PReg file doesn't exist: " << preg_file.value();
+ return false;
+}
+
+bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope,
+ PGROUP_POLICY_OBJECT policy_object_list,
+ base::DictionaryValue* policy) {
+ base::DictionaryValue parsed_policy;
+ base::DictionaryValue forced_policy;
+ for (GROUP_POLICY_OBJECT* policy_object = policy_object_list;
+ policy_object; policy_object = policy_object->pNext) {
+ if (policy_object->dwOptions & GPO_FLAG_DISABLE)
+ continue;
+
+ if (PathIsUNC(policy_object->lpFileSysPath)) {
+ // UNC path: Assume this is an AD-managed machine, which updates the
+ // registry via GPO's standard registry CSE periodically. Fall back to
+ // reading from the registry in this case.
+ return false;
+ }
+
+ base::FilePath preg_file_path(
+ base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName));
+ if (policy_object->dwOptions & GPO_FLAG_FORCE) {
+ base::DictionaryValue new_forced_policy;
+ if (!ReadPRegFile(preg_file_path, &new_forced_policy))
+ return false;
+
+ // Merge with existing forced policy, giving precedence to the existing
+ // forced policy.
+ new_forced_policy.MergeDictionary(&forced_policy);
+ forced_policy.Swap(&new_forced_policy);
+ } else {
+ if (!ReadPRegFile(preg_file_path, &parsed_policy))
+ return false;
+ }
+ }
+
+ // Merge, give precedence to forced policy.
+ parsed_policy.MergeDictionary(&forced_policy);
+ policy->Swap(&parsed_policy);
- // Map of known domain name to their enum values.
+ return true;
+}
+
+
+bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope,
+ base::DictionaryValue* policy) {
+ PGROUP_POLICY_OBJECT policy_object_list = NULL;
+ DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0;
+ if (gpo_provider_->GetAppliedGPOList(
+ flags, NULL, NULL, &kRegistrySettingsCSEGUID,
+ &policy_object_list) != ERROR_SUCCESS) {
+ PLOG(ERROR) << "GetAppliedGPOList scope " << scope;
+ return false;
+ }
+
+ bool result = LoadGPOPolicy(scope, policy_object_list, policy);
+ if (!gpo_provider_->FreeGPOList(policy_object_list))
+ LOG(WARNING) << "FreeGPOList";
+
+ return result;
+}
+
+void PolicyLoaderWin::LoadChromePolicy(const base::DictionaryValue* gpo_dict,
+ PolicyLevel level,
+ PolicyScope scope,
+ PolicyMap* chrome_policy_map) {
+ PolicyMap policy;
+ ParsePolicy(gpo_dict, level, scope, &chrome_policy_schema_, &policy);
+ chrome_policy_map->MergeFrom(policy);
+}
+
+void PolicyLoaderWin::Load3rdPartyPolicy(
+ const DictionaryValue* gpo_dict,
+ PolicyScope scope,
+ PolicyBundle* bundle) {
+ // Map of known 3rd party policy domain name to their enum values.
static const struct {
const char* name;
PolicyDomain domain;
- } kDomains[] = {
- { "extensions", POLICY_DOMAIN_EXTENSIONS },
+ } k3rdPartyDomains[] = {
+ { "extensions", POLICY_DOMAIN_EXTENSIONS },
};
- // Map of policy paths to their corresponding policy level, in decreasing
- // order of priority.
+ // Policy level and corresponding path.
static const struct {
- const char* path;
PolicyLevel level;
- } kKeyPaths[] = {
- { "policy", POLICY_LEVEL_MANDATORY },
- { "recommended", POLICY_LEVEL_RECOMMENDED },
+ const char* path;
+ } kLevels[] = {
+ { POLICY_LEVEL_MANDATORY, kKeyMandatory },
+ { POLICY_LEVEL_RECOMMENDED, kKeyRecommended },
};
- // Path where policies for components are stored.
- const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep +
- kThirdParty + kPathSep;
-
- for (size_t h = 0; h < arraysize(kHives); ++h) {
- HKEY hkey = kHives[h].hive;
-
- for (size_t d = 0; d < arraysize(kDomains); ++d) {
- // Each subkey under this domain is a component of that domain.
- // |domain_path| == SOFTWARE\Policies\Chromium\3rdparty\<domain>
- string16 domain_path = kPathPrefix + ASCIIToUTF16(kDomains[d].name);
-
- for (RegistryKeyIterator domain_iterator(hkey, domain_path.c_str());
- domain_iterator.Valid(); ++domain_iterator) {
- string16 component(domain_iterator.Name());
- string16 component_path = domain_path + kPathSep + component;
-
- // Load the schema for this component's policy, if present.
- scoped_ptr<base::DictionaryValue> schema(
- ReadRegistrySchema(hkey, component_path, kSchema));
-
- for (size_t k = 0; k < arraysize(kKeyPaths); ++k) {
- string16 path =
- component_path + kPathSep + ASCIIToUTF16(kKeyPaths[k].path);
-
- scoped_ptr<base::DictionaryValue> dictionary(
- ReadComponentDictionaryValue(hkey, path, schema.get()));
- if (dictionary.get()) {
- PolicyMap policies;
- policies.LoadFrom(
- dictionary.get(), kKeyPaths[k].level, kHives[h].scope);
- // LoadFrom() overwrites any existing values. Use a temporary map
- // and then use MergeFrom(), that only overwrites values with lower
- // priority.
- bundle->Get(PolicyNamespace(kDomains[d].domain,
- UTF16ToUTF8(component)))
- .MergeFrom(policies);
- }
+ for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) {
+ const char* name = k3rdPartyDomains[i].name;
+ const PolicyDomain domain = k3rdPartyDomains[i].domain;
+ const base::DictionaryValue* domain_dict = NULL;
+ if (!gpo_dict->GetDictionaryWithoutPathExpansion(name, &domain_dict) ||
+ !domain_dict) {
+ continue;
+ }
+
+ for (base::DictionaryValue::Iterator component(*domain_dict);
+ !component.IsAtEnd(); component.Advance()) {
+ const base::DictionaryValue* component_dict = NULL;
+ if (!component.value().GetAsDictionary(&component_dict) ||
+ !component_dict) {
+ continue;
+ }
+
+ // Load the schema.
+ scoped_ptr<base::Value> schema;
+ const base::DictionaryValue* schema_dict = NULL;
+ std::string schema_json;
+ if (component_dict->GetStringWithoutPathExpansion(kKeySchema,
+ &schema_json)) {
+ schema.reset(base::JSONReader::Read(schema_json));
+ if (!schema || !schema->GetAsDictionary(&schema_dict)) {
+ LOG(WARNING) << "Failed to parse 3rd-part policy schema for "
+ << domain << "/" << component.key();
+ }
+ }
+
+ // Parse policy.
+ for (size_t j = 0; j < arraysize(kLevels); j++) {
+ const base::DictionaryValue* policy_dict = NULL;
+ if (!component_dict->GetDictionaryWithoutPathExpansion(
+ kLevels[j].path, &policy_dict) ||
+ !policy_dict) {
+ continue;
}
+
+ PolicyMap policy;
+ ParsePolicy(policy_dict, kLevels[j].level, scope, schema_dict, &policy);
+ PolicyNamespace policy_namespace(domain, component.key());
+ bundle->Get(policy_namespace).MergeFrom(policy);
}
}
}
diff --git a/chrome/browser/policy/policy_loader_win.h b/chrome/browser/policy/policy_loader_win.h
index 4156ffe..096e420 100644
--- a/chrome/browser/policy/policy_loader_win.h
+++ b/chrome/browser/policy/policy_loader_win.h
@@ -5,30 +5,90 @@
#ifndef CHROME_BROWSER_POLICY_POLICY_LOADER_WIN_H_
#define CHROME_BROWSER_POLICY_POLICY_LOADER_WIN_H_
+#include <userenv.h>
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
+#include "base/values.h"
#include "base/win/object_watcher.h"
#include "chrome/browser/policy/async_policy_loader.h"
+#include "chrome/browser/policy/policy_types.h"
+
+namespace base {
+class FilePath;
+}
namespace policy {
-struct PolicyDefinitionList;
+class AppliedGPOListProvider;
class PolicyMap;
+struct PolicyDefinitionList;
+
+// Interface for mocking out GPO enumeration in tests.
+class AppliedGPOListProvider {
+ public:
+ virtual ~AppliedGPOListProvider() {}
+ virtual DWORD GetAppliedGPOList(DWORD flags,
+ LPCTSTR machine_name,
+ PSID sid_user,
+ GUID* extension_guid,
+ PGROUP_POLICY_OBJECT* gpo_list) = 0;
+ virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) = 0;
+};
// Loads policies from the Windows registry, and watches for Group Policy
// notifications to trigger reloads.
class PolicyLoaderWin : public AsyncPolicyLoader,
public base::win::ObjectWatcher::Delegate {
public:
- explicit PolicyLoaderWin(const PolicyDefinitionList* policy_list);
+ explicit PolicyLoaderWin(const PolicyDefinitionList* policy_list,
+ const string16& chrome_policy_key,
+ AppliedGPOListProvider* gpo_provider);
virtual ~PolicyLoaderWin();
+ // Creates a policy loader that uses the Win API to access GPO.
+ static scoped_ptr<PolicyLoaderWin> Create(
+ const PolicyDefinitionList* policy_list);
+
// AsyncPolicyLoader implementation.
virtual void InitOnFile() OVERRIDE;
virtual scoped_ptr<PolicyBundle> Load() OVERRIDE;
private:
- void LoadChromePolicy(PolicyMap* chrome_policies);
- void Load3rdPartyPolicies(PolicyBundle* bundle);
+ // Builds the Chrome policy schema in |chrome_policy_schema_|.
+ void BuildChromePolicySchema();
+
+ // Reads Chrome Policy from a PReg file at the given path and stores the
+ // result in |policy|.
+ bool ReadPRegFile(const base::FilePath& preg_file,
+ base::DictionaryValue* policy);
+
+ // Loads and parses GPO policy in |policy_object_list| for scope |scope|. If
+ // successful, stores the result in |policy| and returns true. Returns false
+ // on failure reading the policy, indicating that policy loading should fall
+ // back to reading the registry.
+ bool LoadGPOPolicy(PolicyScope scope,
+ PGROUP_POLICY_OBJECT policy_object_list,
+ base::DictionaryValue* policy);
+
+ // Queries Windows for applied group policy and writes the result to |policy|.
+ // This is the preferred way to obtain GPO data, there are reports of abuse
+ // of the registry GPO keys by 3rd-party software.
+ bool ReadPolicyFromGPO(PolicyScope scope, base::DictionaryValue* policy);
+
+ // Parses Chrome policy from |gpo_dict| for the given |scope| and |level| and
+ // merges it into |chrome_policy_map|.
+ void LoadChromePolicy(const base::DictionaryValue* gpo_dict,
+ PolicyLevel level,
+ PolicyScope scope,
+ PolicyMap* chrome_policy_map);
+
+ // Loads 3rd-party policy from |gpo_dict| and merges it into |bundle|.
+ void Load3rdPartyPolicy(const base::DictionaryValue* gpo_dict,
+ PolicyScope scope,
+ PolicyBundle* bundle);
// Installs the watchers for the Group Policy update events.
void SetupWatches();
@@ -38,6 +98,9 @@ class PolicyLoaderWin : public AsyncPolicyLoader,
bool is_initialized_;
const PolicyDefinitionList* policy_list_;
+ const string16 chrome_policy_key_;
+ class AppliedGPOListProvider* gpo_provider_;
+ base::DictionaryValue chrome_policy_schema_;
base::WaitableEvent user_policy_changed_event_;
base::WaitableEvent machine_policy_changed_event_;
@@ -49,23 +112,6 @@ class PolicyLoaderWin : public AsyncPolicyLoader,
DISALLOW_COPY_AND_ASSIGN(PolicyLoaderWin);
};
-// Constants shared with tests.
-namespace registry_constants {
- // Path separator for registry keys.
- extern const wchar_t kPathSep[];
- // Registry key within Chrome's key that contains 3rd party policy.
- extern const wchar_t kThirdParty[];
- // Registry key within an extension's namespace that contains mandatory
- // policy.
- extern const wchar_t kMandatory[];
- // Registry key within an extension's namespace that contains recommended
- // policy.
- extern const wchar_t kRecommended[];
- // Registry key within an extension's namespace that contains the policy
- // schema.
- extern const wchar_t kSchema[];
-} // namespace registry_constants
-
} // namespace policy
#endif // CHROME_BROWSER_POLICY_POLICY_LOADER_WIN_H_
diff --git a/chrome/browser/policy/policy_loader_win_unittest.cc b/chrome/browser/policy/policy_loader_win_unittest.cc
index 5193063..c2481bc 100644
--- a/chrome/browser/policy/policy_loader_win_unittest.cc
+++ b/chrome/browser/policy/policy_loader_win_unittest.cc
@@ -4,9 +4,15 @@
#include "chrome/browser/policy/policy_loader_win.h"
+#include <userenv.h>
#include <windows.h>
+#include <cstring>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
#include "base/json/json_writer.h"
+#include "base/path_service.h"
#include "base/process.h"
#include "base/string16.h"
#include "base/string_util.h"
@@ -18,6 +24,7 @@
#include "chrome/browser/policy/configuration_policy_provider_test.h"
#include "chrome/browser/policy/policy_bundle.h"
#include "chrome/browser/policy/policy_map.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/json_schema/json_schema_constants.h"
#include "policy/policy_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,12 +32,18 @@
namespace schema = json_schema_constants;
using base::win::RegKey;
-using namespace policy::registry_constants;
namespace policy {
namespace {
+// Constants for registry key names.
+const wchar_t kPathSep[] = L"\\";
+const wchar_t kThirdParty[] = L"3rdparty";
+const wchar_t kMandatory[] = L"policy";
+const wchar_t kRecommended[] = L"recommended";
+const wchar_t kSchema[] = L"schema";
+
// Installs |value| in the given registry |path| and |hive|, under the key
// |name|. Returns false on errors.
// Some of the possible Value types are stored after a conversion (e.g. doubles
@@ -219,13 +232,15 @@ class ScopedGroupPolicyRegistrySandbox {
DISALLOW_COPY_AND_ASSIGN(ScopedGroupPolicyRegistrySandbox);
};
-class TestHarness : public PolicyProviderTestHarness {
+class TestHarness : public PolicyProviderTestHarness,
+ public AppliedGPOListProvider {
public:
explicit TestHarness(HKEY hive, PolicyScope scope);
virtual ~TestHarness();
virtual void SetUp() OVERRIDE;
+ // PolicyProviderTestHarness:
virtual ConfigurationPolicyProvider* CreateProvider(
const PolicyDefinitionList* policy_definition_list) OVERRIDE;
@@ -245,6 +260,14 @@ class TestHarness : public PolicyProviderTestHarness {
virtual void Install3rdPartyPolicy(
const base::DictionaryValue* policies) OVERRIDE;
+ // AppliedGPOListProvider:
+ virtual DWORD GetAppliedGPOList(DWORD flags,
+ LPCTSTR machine_name,
+ PSID sid_user,
+ GUID* extension_guid,
+ PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE;
+ virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE;
+
// Creates a harness instance that will install policy in HKCU or HKLM,
// respectively.
static PolicyProviderTestHarness* CreateHKCU();
@@ -310,9 +333,23 @@ TestHarness::~TestHarness() {}
void TestHarness::SetUp() {}
+DWORD TestHarness::GetAppliedGPOList(DWORD flags,
+ LPCTSTR machine_name,
+ PSID sid_user,
+ GUID* extension_guid,
+ PGROUP_POLICY_OBJECT* gpo_list) {
+ *gpo_list = NULL;
+ return ERROR_ACCESS_DENIED;
+}
+
+BOOL TestHarness::FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) {
+ return TRUE;
+}
+
ConfigurationPolicyProvider* TestHarness::CreateProvider(
const PolicyDefinitionList* policy_list) {
- scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin(policy_list));
+ scoped_ptr<AsyncPolicyLoader> loader(
+ new PolicyLoaderWin(policy_list, kRegistryChromePolicyKey, this));
return new AsyncPolicyProvider(loader.Pass());
}
@@ -320,7 +357,7 @@ void TestHarness::InstallEmptyPolicy() {}
void TestHarness::InstallStringPolicy(const std::string& policy_name,
const std::string& policy_value) {
- RegKey key(hive_, kRegistryMandatorySubKey, KEY_ALL_ACCESS);
+ RegKey key(hive_, kRegistryChromePolicyKey, KEY_ALL_ACCESS);
ASSERT_TRUE(key.Valid());
ASSERT_HRESULT_SUCCEEDED(key.WriteValue(UTF8ToUTF16(policy_name).c_str(),
UTF8ToUTF16(policy_value).c_str()));
@@ -328,7 +365,7 @@ void TestHarness::InstallStringPolicy(const std::string& policy_name,
void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
int policy_value) {
- RegKey key(hive_, kRegistryMandatorySubKey, KEY_ALL_ACCESS);
+ RegKey key(hive_, kRegistryChromePolicyKey, KEY_ALL_ACCESS);
ASSERT_TRUE(key.Valid());
key.WriteValue(UTF8ToUTF16(policy_name).c_str(),
static_cast<DWORD>(policy_value));
@@ -336,7 +373,7 @@ void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
bool policy_value) {
- RegKey key(hive_, kRegistryMandatorySubKey, KEY_ALL_ACCESS);
+ RegKey key(hive_, kRegistryChromePolicyKey, KEY_ALL_ACCESS);
ASSERT_TRUE(key.Valid());
key.WriteValue(UTF8ToUTF16(policy_name).c_str(),
static_cast<DWORD>(policy_value));
@@ -345,7 +382,7 @@ void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
void TestHarness::InstallStringListPolicy(const std::string& policy_name,
const base::ListValue* policy_value) {
RegKey key(hive_,
- (string16(kRegistryMandatorySubKey) + ASCIIToUTF16("\\") +
+ (string16(kRegistryChromePolicyKey) + ASCIIToUTF16("\\") +
UTF8ToUTF16(policy_name)).c_str(),
KEY_ALL_ACCESS);
ASSERT_TRUE(key.Valid());
@@ -367,7 +404,7 @@ void TestHarness::InstallDictionaryPolicy(
const base::DictionaryValue* policy_value) {
std::string json;
base::JSONWriter::Write(policy_value, &json);
- RegKey key(hive_, kRegistryMandatorySubKey, KEY_ALL_ACCESS);
+ RegKey key(hive_, kRegistryChromePolicyKey, KEY_ALL_ACCESS);
ASSERT_TRUE(key.Valid());
key.WriteValue(UTF8ToUTF16(policy_name).c_str(),
UTF8ToUTF16(json).c_str());
@@ -376,7 +413,7 @@ void TestHarness::InstallDictionaryPolicy(
void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
// The first level entries are domains, and the second level entries map
// components to their policy.
- const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep +
+ const string16 kPathPrefix = string16(kRegistryChromePolicyKey) + kPathSep +
kThirdParty + kPathSep;
for (base::DictionaryValue::Iterator domain(*policies);
!domain.IsAtEnd(); domain.Advance()) {
@@ -387,7 +424,7 @@ void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
}
for (base::DictionaryValue::Iterator component(*components);
!component.IsAtEnd(); component.Advance()) {
- const string16 path = string16(kRegistryMandatorySubKey) + kPathSep +
+ const string16 path = string16(kRegistryChromePolicyKey) + kPathSep +
kThirdParty + kPathSep +
UTF8ToUTF16(domain.key()) + kPathSep +
UTF8ToUTF16(component.key());
@@ -422,26 +459,104 @@ INSTANTIATE_TEST_CASE_P(
testing::Values(TestHarness::CreateHKCU, TestHarness::CreateHKLM));
// Test cases for windows policy provider specific functionality.
-class PolicyLoaderWinTest : public PolicyTestBase {
+class PolicyLoaderWinTest : public PolicyTestBase,
+ public AppliedGPOListProvider {
protected:
- PolicyLoaderWinTest() {}
+ // The policy key this tests places data under. This must match the data
+ // files in chrome/test/data/policy/gpo.
+ static const char16 kTestPolicyKey[];
+
+ PolicyLoaderWinTest()
+ : gpo_list_(NULL),
+ gpo_list_status_(ERROR_ACCESS_DENIED) {}
virtual ~PolicyLoaderWinTest() {}
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_));
+ test_data_dir_ = test_data_dir_.AppendASCII("policy").AppendASCII("gpo");
+ }
+
+ // AppliedGPOListProvider:
+ virtual DWORD GetAppliedGPOList(DWORD flags,
+ LPCTSTR machine_name,
+ PSID sid_user,
+ GUID* extension_guid,
+ PGROUP_POLICY_OBJECT* gpo_list) OVERRIDE {
+ *gpo_list = gpo_list_;
+ return gpo_list_status_;
+ }
+ virtual BOOL FreeGPOList(PGROUP_POLICY_OBJECT gpo_list) OVERRIDE {
+ return TRUE;
+ }
+
+ void InitGPO(GROUP_POLICY_OBJECT* gpo,
+ DWORD options,
+ const base::FilePath& path,
+ GROUP_POLICY_OBJECT* next,
+ GROUP_POLICY_OBJECT* prev) {
+ memset(gpo, 0, sizeof(GROUP_POLICY_OBJECT));
+ gpo->dwOptions = options;
+ gpo->lpFileSysPath = const_cast<wchar_t*>(path.value().c_str());
+ gpo->pNext = next;
+ gpo->pPrev = prev;
+ }
+
bool Matches(const PolicyBundle& expected) {
- PolicyLoaderWin loader(&test_policy_definitions::kList);
+ PolicyLoaderWin loader(&test_policy_definitions::kList, kTestPolicyKey,
+ this);
scoped_ptr<PolicyBundle> loaded(loader.Load());
return loaded->Equals(expected);
}
+ void InstallRegistrySentinel() {
+ RegKey hklm_key(HKEY_CURRENT_USER, kTestPolicyKey, KEY_ALL_ACCESS);
+ ASSERT_TRUE(hklm_key.Valid());
+ hklm_key.WriteValue(
+ UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(),
+ UTF8ToUTF16("registry").c_str());
+ }
+
+ bool MatchesRegistrySentinel() {
+ base::DictionaryValue expected_policy;
+ expected_policy.SetString(test_policy_definitions::kKeyString, "registry");
+ PolicyBundle expected;
+ expected.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+ .LoadFrom(&expected_policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
+ return Matches(expected);
+ }
+
+ bool MatchesTestBundle() {
+ base::DictionaryValue expected_policy;
+ expected_policy.SetBoolean(test_policy_definitions::kKeyBoolean, true);
+ expected_policy.SetString(test_policy_definitions::kKeyString, "GPO");
+ expected_policy.SetInteger(test_policy_definitions::kKeyInteger, 42);
+ scoped_ptr<base::ListValue> list(new base::ListValue());
+ list->AppendString("GPO 1");
+ list->AppendString("GPO 2");
+ expected_policy.Set(test_policy_definitions::kKeyStringList,
+ list.release());
+ PolicyBundle expected;
+ expected.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+ .LoadFrom(&expected_policy, POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_MACHINE);
+ return Matches(expected);
+ }
+
ScopedGroupPolicyRegistrySandbox registry_sandbox_;
+ PGROUP_POLICY_OBJECT gpo_list_;
+ DWORD gpo_list_status_;
+ base::FilePath test_data_dir_;
};
+const char16 PolicyLoaderWinTest::kTestPolicyKey[] =
+ L"SOFTWARE\\Policies\\Chromium";
+
TEST_F(PolicyLoaderWinTest, HKLMOverHKCU) {
- RegKey hklm_key(HKEY_LOCAL_MACHINE, kRegistryMandatorySubKey, KEY_ALL_ACCESS);
+ RegKey hklm_key(HKEY_LOCAL_MACHINE, kTestPolicyKey, KEY_ALL_ACCESS);
ASSERT_TRUE(hklm_key.Valid());
hklm_key.WriteValue(UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(),
UTF8ToUTF16("hklm").c_str());
- RegKey hkcu_key(HKEY_CURRENT_USER, kRegistryMandatorySubKey, KEY_ALL_ACCESS);
+ RegKey hkcu_key(HKEY_CURRENT_USER, kTestPolicyKey, KEY_ALL_ACCESS);
ASSERT_TRUE(hkcu_key.Valid());
hkcu_key.WriteValue(UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(),
UTF8ToUTF16("hkcu").c_str());
@@ -469,7 +584,7 @@ TEST_F(PolicyLoaderWinTest, Load3rdPartyWithoutSchema) {
policy_dict.Set("extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.policy",
dict.DeepCopy());
EXPECT_TRUE(InstallValue(policy_dict, HKEY_LOCAL_MACHINE,
- kRegistryMandatorySubKey, kThirdParty));
+ kTestPolicyKey, kThirdParty));
PolicyBundle expected;
expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
@@ -486,7 +601,7 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
// combinations, to verify that they overlap as expected.
const string16 kPathSuffix =
- kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\merge");
+ kTestPolicyKey + ASCIIToUTF16("\\3rdparty\\extensions\\merge");
const char kUserMandatory[] = "user-mandatory";
const char kUserRecommended[] = "user-recommended";
@@ -558,7 +673,7 @@ TEST_F(PolicyLoaderWinTest, LoadStringEncodedValues) {
encoded_policy.SetString("dict", encoded_dict);
const string16 kPathSuffix =
- kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\string");
+ kTestPolicyKey + ASCIIToUTF16("\\3rdparty\\extensions\\string");
EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema));
EXPECT_TRUE(
InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
@@ -581,7 +696,7 @@ TEST_F(PolicyLoaderWinTest, LoadIntegerEncodedValues) {
encoded_policy.SetInteger("double", 456);
const string16 kPathSuffix =
- kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\int");
+ kTestPolicyKey + ASCIIToUTF16("\\3rdparty\\extensions\\int");
EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema));
EXPECT_TRUE(
InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
@@ -607,7 +722,7 @@ TEST_F(PolicyLoaderWinTest, DefaultPropertySchemaType) {
schema.Set(schema::kAdditionalProperties, default_schema.DeepCopy());
const string16 kPathSuffix =
- kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\test");
+ kTestPolicyKey + ASCIIToUTF16("\\3rdparty\\extensions\\test");
EXPECT_TRUE(WriteSchema(schema, HKEY_CURRENT_USER, kPathSuffix, kSchema));
// Write some test values.
@@ -626,10 +741,120 @@ TEST_F(PolicyLoaderWinTest, DefaultPropertySchemaType) {
expected_policy.SetInteger("special-int2", -456);
expected_policy.SetDouble("double1", 789.0);
expected_policy.SetDouble("double2", 123.456e7);
+ expected_policy.Set("invalid", base::Value::CreateNullValue());
PolicyBundle expected;
expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "test"))
.LoadFrom(&expected_policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
EXPECT_TRUE(Matches(expected));
}
+TEST_F(PolicyLoaderWinTest, AppliedPolicyNotPresent) {
+ InstallRegistrySentinel();
+ gpo_list_ = NULL;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ PolicyBundle empty;
+ EXPECT_TRUE(Matches(empty));
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyEmpty) {
+ InstallRegistrySentinel();
+ base::FilePath gpo_dir(test_data_dir_.AppendASCII("empty"));
+ GROUP_POLICY_OBJECT gpo;
+ InitGPO(&gpo, 0, gpo_dir, NULL, NULL);
+ gpo_list_ = &gpo;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ PolicyBundle empty;
+ EXPECT_TRUE(Matches(empty));
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyNonExistingFile) {
+ InstallRegistrySentinel();
+ GROUP_POLICY_OBJECT gpo;
+ InitGPO(&gpo, 0, test_data_dir_, NULL, NULL);
+ gpo_list_ = &gpo;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesRegistrySentinel());
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyBadPath) {
+ InstallRegistrySentinel();
+ base::FilePath gpo_dir(test_data_dir_.AppendASCII("bad"));
+ GROUP_POLICY_OBJECT gpo;
+ InitGPO(&gpo, 0, gpo_dir, NULL, NULL);
+ gpo_list_ = &gpo;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesRegistrySentinel());
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyPresent) {
+ InstallRegistrySentinel();
+ base::FilePath gpo_dir(test_data_dir_.AppendASCII("test1"));
+ GROUP_POLICY_OBJECT gpo;
+ InitGPO(&gpo, 0, gpo_dir, NULL, NULL);
+ gpo_list_ = &gpo;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesTestBundle());
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyMerged) {
+ InstallRegistrySentinel();
+ base::FilePath gpo1_dir(test_data_dir_.AppendASCII("test2"));
+ base::FilePath gpo2_dir(test_data_dir_.AppendASCII("test1"));
+ GROUP_POLICY_OBJECT gpo1;
+ GROUP_POLICY_OBJECT gpo2;
+ InitGPO(&gpo1, 0, gpo1_dir, &gpo2, NULL);
+ InitGPO(&gpo2, 0, gpo2_dir, NULL, &gpo1);
+ gpo_list_ = &gpo1;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesTestBundle());
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyDisabled) {
+ InstallRegistrySentinel();
+ base::FilePath gpo1_dir(test_data_dir_.AppendASCII("test1"));
+ base::FilePath gpo2_dir(test_data_dir_.AppendASCII("test2"));
+ GROUP_POLICY_OBJECT gpo1;
+ GROUP_POLICY_OBJECT gpo2;
+ InitGPO(&gpo1, 0, gpo1_dir, &gpo2, NULL);
+ InitGPO(&gpo2, GPO_FLAG_DISABLE, gpo2_dir, NULL, &gpo1);
+ gpo_list_ = &gpo1;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesTestBundle());
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyForcedPolicy) {
+ InstallRegistrySentinel();
+ base::FilePath gpo1_dir(test_data_dir_.AppendASCII("test1"));
+ base::FilePath gpo2_dir(test_data_dir_.AppendASCII("test2"));
+ GROUP_POLICY_OBJECT gpo1;
+ GROUP_POLICY_OBJECT gpo2;
+ InitGPO(&gpo1, GPO_FLAG_FORCE, gpo1_dir, &gpo2, NULL);
+ InitGPO(&gpo2, 0, gpo2_dir, NULL, &gpo1);
+ gpo_list_ = &gpo1;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesTestBundle());
+}
+
+TEST_F(PolicyLoaderWinTest, AppliedPolicyUNCPath) {
+ InstallRegistrySentinel();
+ base::FilePath gpo_dir(test_data_dir_.AppendASCII("test1"));
+ base::FilePath unc_path(L"\\\\some_share\\GPO");
+ GROUP_POLICY_OBJECT gpo1;
+ GROUP_POLICY_OBJECT gpo2;
+ InitGPO(&gpo1, 0, gpo_dir, &gpo2, NULL);
+ InitGPO(&gpo2, 0, unc_path, NULL, &gpo1);
+ gpo_list_ = &gpo1;
+ gpo_list_status_ = ERROR_SUCCESS;
+
+ EXPECT_TRUE(MatchesRegistrySentinel());
+}
+
} // namespace policy
diff --git a/chrome/browser/policy/policy_path_parser_win.cc b/chrome/browser/policy/policy_path_parser_win.cc
index c6c5ed9..b8216bc 100644
--- a/chrome/browser/policy/policy_path_parser_win.cc
+++ b/chrome/browser/policy/policy_path_parser_win.cc
@@ -25,7 +25,7 @@ bool LoadUserDataDirPolicyFromRegistry(HKEY hive,
std::wstring value;
base::win::RegKey policy_key(hive,
- policy::kRegistryMandatorySubKey,
+ policy::kRegistryChromePolicyKey,
KEY_READ);
if (policy_key.ReadValue(key_name.c_str(), &value) == ERROR_SUCCESS) {
*user_data_dir =
diff --git a/chrome/tools/build/generate_policy_source.py b/chrome/tools/build/generate_policy_source.py
index 5809e84..0892933 100755
--- a/chrome/tools/build/generate_policy_source.py
+++ b/chrome/tools/build/generate_policy_source.py
@@ -17,10 +17,8 @@ import sys
import textwrap
-CHROME_MANDATORY_SUBKEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome'
-CHROME_RECOMMENDED_SUBKEY = CHROME_MANDATORY_SUBKEY + '\\\\Recommended'
-CHROMIUM_MANDATORY_SUBKEY = 'SOFTWARE\\\\Policies\\\\Chromium'
-CHROMIUM_RECOMMENDED_SUBKEY = CHROMIUM_MANDATORY_SUBKEY + '\\\\Recommended'
+CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome'
+CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium'
class PolicyDetails:
@@ -193,12 +191,9 @@ def _WritePolicyConstantHeader(policies, os, f):
'namespace policy {\n\n')
if os == 'win':
- f.write('// The windows registry path where mandatory policy '
+ f.write('// The windows registry path where Chrome policy '
'configuration resides.\n'
- 'extern const wchar_t kRegistryMandatorySubKey[];\n'
- '// The windows registry path where recommended policy '
- 'configuration resides.\n'
- 'extern const wchar_t kRegistryRecommendedSubKey[];\n\n')
+ 'extern const wchar_t kRegistryChromePolicyKey[];\n')
f.write('// Lists policy types mapped to their names and expected types.\n'
'// Used to initialize ConfigurationPolicyProviders.\n'
@@ -267,15 +262,11 @@ def _WritePolicyConstantSource(policies, os, f):
if os == 'win':
f.write('#if defined(GOOGLE_CHROME_BUILD)\n'
- 'const wchar_t kRegistryMandatorySubKey[] = '
- 'L"' + CHROME_MANDATORY_SUBKEY + '";\n'
- 'const wchar_t kRegistryRecommendedSubKey[] = '
- 'L"' + CHROME_RECOMMENDED_SUBKEY + '";\n'
+ 'const wchar_t kRegistryChromePolicyKey[] = '
+ 'L"' + CHROME_POLICY_KEY + '";\n'
'#else\n'
- 'const wchar_t kRegistryMandatorySubKey[] = '
- 'L"' + CHROMIUM_MANDATORY_SUBKEY + '";\n'
- 'const wchar_t kRegistryRecommendedSubKey[] = '
- 'L"' + CHROMIUM_RECOMMENDED_SUBKEY + '";\n'
+ 'const wchar_t kRegistryChromePolicyKey[] = '
+ 'L"' + CHROMIUM_POLICY_KEY + '";\n'
'#endif\n\n')
f.write('bool IsDeprecatedPolicy(const std::string& policy) {\n'
diff --git a/chrome_frame/chrome_launcher.cc b/chrome_frame/chrome_launcher.cc
index 88fc0da..28613f4 100644
--- a/chrome_frame/chrome_launcher.cc
+++ b/chrome_frame/chrome_launcher.cc
@@ -164,7 +164,7 @@ void AppendAdditionalLaunchParameters(std::wstring* command_line) {
LONG result;
bool found = false;
for (int i = 0; !found && i < arraysize(kRootKeys); ++i) {
- result = ::RegOpenKeyExW(kRootKeys[i], policy::kRegistryMandatorySubKey, 0,
+ result = ::RegOpenKeyExW(kRootKeys[i], policy::kRegistryChromePolicyKey, 0,
KEY_QUERY_VALUE, &key);
if (result == ERROR_SUCCESS) {
DWORD size = 0;
diff --git a/chrome_frame/policy_settings.cc b/chrome_frame/policy_settings.cc
index bc873e8..846fd99 100644
--- a/chrome_frame/policy_settings.cc
+++ b/chrome_frame/policy_settings.cc
@@ -74,7 +74,7 @@ void PolicySettings::ReadUrlSettings(
std::wstring settings_value(
ASCIIToWide(policy::key::kChromeFrameRendererSettings));
for (int i = 0; i < arraysize(kRootKeys); ++i) {
- if ((config_key.Open(kRootKeys[i], policy::kRegistryMandatorySubKey,
+ if ((config_key.Open(kRootKeys[i], policy::kRegistryChromePolicyKey,
KEY_READ) == ERROR_SUCCESS) &&
(config_key.ReadValueDW(settings_value.c_str(),
&value) == ERROR_SUCCESS)) {
@@ -109,7 +109,7 @@ void PolicySettings::ReadContentTypeSetting(
std::vector<std::wstring>* content_type_list) {
DCHECK(content_type_list);
- std::wstring sub_key(policy::kRegistryMandatorySubKey);
+ std::wstring sub_key(policy::kRegistryChromePolicyKey);
sub_key += L"\\";
sub_key += ASCIIToWide(policy::key::kChromeFrameContentTypes);
@@ -128,7 +128,7 @@ void PolicySettings::ReadStringSetting(const char* value_name,
base::win::RegKey config_key;
std::wstring value_name_str(ASCIIToWide(value_name));
for (int i = 0; i < arraysize(kRootKeys); ++i) {
- if ((config_key.Open(kRootKeys[i], policy::kRegistryMandatorySubKey,
+ if ((config_key.Open(kRootKeys[i], policy::kRegistryChromePolicyKey,
KEY_READ) == ERROR_SUCCESS) &&
(config_key.ReadValue(value_name_str.c_str(),
value) == ERROR_SUCCESS)) {
diff --git a/chrome_frame/test/policy_settings_unittest.cc b/chrome_frame/test/policy_settings_unittest.cc
index e13af50..343b426 100644
--- a/chrome_frame/test/policy_settings_unittest.cc
+++ b/chrome_frame/test/policy_settings_unittest.cc
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/basictypes.h"
#include "base/at_exit.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stringprintf.h"
@@ -23,7 +23,7 @@ namespace {
// A best effort way to zap CF policy entries that may be in the registry.
void DeleteChromeFramePolicyEntries(HKEY root) {
RegKey key;
- if (key.Open(root, policy::kRegistryMandatorySubKey,
+ if (key.Open(root, policy::kRegistryChromePolicyKey,
KEY_ALL_ACCESS) == ERROR_SUCCESS) {
key.DeleteValue(
ASCIIToWide(policy::key::kChromeFrameRendererSettings).c_str());
@@ -36,7 +36,7 @@ void DeleteChromeFramePolicyEntries(HKEY root) {
bool InitializePolicyKey(HKEY policy_root, RegKey* policy_key) {
EXPECT_EQ(ERROR_SUCCESS, policy_key->Create(policy_root,
- policy::kRegistryMandatorySubKey, KEY_ALL_ACCESS));
+ policy::kRegistryChromePolicyKey, KEY_ALL_ACCESS));
return policy_key->Valid();
}