| OLD | NEW |
| (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/configuration_policy_provider_delegate_win.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include <string.h> | |
| 10 | |
| 11 #include "base/basictypes.h" | |
| 12 #include "base/json/json_reader.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/memory/scoped_ptr.h" | |
| 15 #include "base/string_number_conversions.h" | |
| 16 #include "base/utf_string_conversions.h" | |
| 17 #include "base/values.h" | |
| 18 #include "base/win/registry.h" | |
| 19 #include "chrome/browser/policy/policy_bundle.h" | |
| 20 #include "chrome/browser/policy/policy_map.h" | |
| 21 #include "policy/policy_constants.h" | |
| 22 | |
| 23 using base::win::RegKey; | |
| 24 | |
| 25 namespace policy { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // Determines the registry key with the highest priority that contains | |
| 30 // the |key_path| key with a |value_name| value inside. | |
| 31 // |key_path| is a suffix to the Chrome mandatory or recommended registry key, | |
| 32 // and can be empty to lookup values directly at that key. | |
| 33 // |value_name| is the name of a value that should exist at that path. If it | |
| 34 // is empty, then only the existence of the path is verified. | |
| 35 // Returns true if |key| was updated to point to a key with a |key_path| suffix | |
| 36 // and optional |value_name| value inside. In that case, |level| and |scope| | |
| 37 // will contain the appropriate values for the key found. | |
| 38 // Returns false otherwise. | |
| 39 bool LoadHighestPriorityKey(const string16& key_path, | |
| 40 const string16& value_name, | |
| 41 RegKey* key, | |
| 42 PolicyLevel* level, | |
| 43 PolicyScope* scope) { | |
| 44 // |path| is in decreasing order of priority. | |
| 45 static const struct { | |
| 46 const wchar_t* path; | |
| 47 PolicyLevel level; | |
| 48 } key_paths[] = { | |
| 49 { kRegistryMandatorySubKey, POLICY_LEVEL_MANDATORY }, | |
| 50 { kRegistryRecommendedSubKey, POLICY_LEVEL_RECOMMENDED }, | |
| 51 }; | |
| 52 | |
| 53 // |hive| is in decreasing order of priority. | |
| 54 static const struct { | |
| 55 HKEY hive; | |
| 56 PolicyScope scope; | |
| 57 } hives[] = { | |
| 58 { HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE }, | |
| 59 { HKEY_CURRENT_USER, POLICY_SCOPE_USER }, | |
| 60 }; | |
| 61 | |
| 62 // Lookup at the mandatory path for both user and machine policies first, and | |
| 63 // then at the recommended path. | |
| 64 for (size_t k = 0; k < arraysize(key_paths); ++k) { | |
| 65 for (size_t h = 0; h < arraysize(hives); ++h) { | |
| 66 string16 path(key_paths[k].path); | |
| 67 if (!key_path.empty()) | |
| 68 path += ASCIIToUTF16("\\") + key_path; | |
| 69 key->Open(hives[h].hive, path.c_str(), KEY_READ); | |
| 70 if (!key->Valid()) | |
| 71 continue; | |
| 72 if (value_name.empty() || key->HasValue(value_name.c_str())) { | |
| 73 *level = key_paths[k].level; | |
| 74 *scope = hives[h].scope; | |
| 75 return true; | |
| 76 } | |
| 77 } | |
| 78 } | |
| 79 return false; | |
| 80 } | |
| 81 | |
| 82 // Reads a REG_SZ string at |key| named |name| into |result|. Returns false if | |
| 83 // the string could not be read. | |
| 84 bool ReadRegistryString(RegKey* key, | |
| 85 const string16& name, | |
| 86 string16* result) { | |
| 87 DWORD value_size = 0; | |
| 88 DWORD key_type = 0; | |
| 89 scoped_array<uint8> buffer; | |
| 90 | |
| 91 if (key->ReadValue(name.c_str(), 0, &value_size, &key_type) != ERROR_SUCCESS) | |
| 92 return false; | |
| 93 if (key_type != REG_SZ) | |
| 94 return false; | |
| 95 | |
| 96 // According to the Microsoft documentation, the string | |
| 97 // buffer may not be explicitly 0-terminated. Allocate a | |
| 98 // slightly larger buffer and pre-fill to zeros to guarantee | |
| 99 // the 0-termination. | |
| 100 buffer.reset(new uint8[value_size + 2]); | |
| 101 memset(buffer.get(), 0, value_size + 2); | |
| 102 key->ReadValue(name.c_str(), buffer.get(), &value_size, NULL); | |
| 103 result->assign(reinterpret_cast<const wchar_t*>(buffer.get())); | |
| 104 return true; | |
| 105 } | |
| 106 | |
| 107 // Reads a REG_DWORD integer at |key| named |name| into |result|. Returns false | |
| 108 // if the value could no be read. | |
| 109 bool ReadRegistryInteger(RegKey* key, | |
| 110 const string16& name, | |
| 111 uint32* result) { | |
| 112 DWORD dword; | |
| 113 if (key->ReadValueDW(name.c_str(), &dword) != ERROR_SUCCESS) | |
| 114 return false; | |
| 115 *result = dword; | |
| 116 return true; | |
| 117 } | |
| 118 | |
| 119 // Returns the Value for a Chrome string policy named |name|, or NULL if | |
| 120 // it wasn't found. The caller owns the returned value. | |
| 121 base::Value* ReadStringValue(const string16& name, | |
| 122 PolicyLevel* level, | |
| 123 PolicyScope* scope) { | |
| 124 RegKey key; | |
| 125 if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) | |
| 126 return NULL; | |
| 127 string16 value; | |
| 128 if (!ReadRegistryString(&key, name, &value)) | |
| 129 return NULL; | |
| 130 return base::Value::CreateStringValue(value); | |
| 131 } | |
| 132 | |
| 133 // Returns the Value for a Chrome string list policy named |name|, | |
| 134 // or NULL if it wasn't found. The caller owns the returned value. | |
| 135 base::Value* ReadStringListValue(const string16& name, | |
| 136 PolicyLevel* level, | |
| 137 PolicyScope* scope) { | |
| 138 RegKey key; | |
| 139 if (!LoadHighestPriorityKey(name, string16(), &key, level, scope)) | |
| 140 return NULL; | |
| 141 base::ListValue* result = new base::ListValue(); | |
| 142 string16 value; | |
| 143 int index = 0; | |
| 144 while (ReadRegistryString(&key, base::IntToString16(++index), &value)) | |
| 145 result->Append(base::Value::CreateStringValue(value)); | |
| 146 return result; | |
| 147 } | |
| 148 | |
| 149 // Returns the Value for a Chrome boolean policy named |name|, | |
| 150 // or NULL if it wasn't found. The caller owns the returned value. | |
| 151 base::Value* ReadBooleanValue(const string16& name, | |
| 152 PolicyLevel* level, | |
| 153 PolicyScope* scope) { | |
| 154 RegKey key; | |
| 155 if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) | |
| 156 return NULL; | |
| 157 uint32 value; | |
| 158 if (!ReadRegistryInteger(&key, name, &value)) | |
| 159 return NULL; | |
| 160 return base::Value::CreateBooleanValue(value != 0u); | |
| 161 } | |
| 162 | |
| 163 // Returns the Value for a Chrome integer policy named |name|, | |
| 164 // or NULL if it wasn't found. The caller owns the returned value. | |
| 165 base::Value* ReadIntegerValue(const string16& name, | |
| 166 PolicyLevel* level, | |
| 167 PolicyScope* scope) { | |
| 168 RegKey key; | |
| 169 if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) | |
| 170 return NULL; | |
| 171 uint32 value; | |
| 172 if (!ReadRegistryInteger(&key, name, &value)) | |
| 173 return NULL; | |
| 174 return base::Value::CreateIntegerValue(value); | |
| 175 } | |
| 176 | |
| 177 // Returns the Value for a Chrome dictionary policy named |name|, | |
| 178 // or NULL if it wasn't found. The caller owns the returned value. | |
| 179 base::Value* ReadDictionaryValue(const string16& name, | |
| 180 PolicyLevel* level, | |
| 181 PolicyScope* scope) { | |
| 182 // Dictionaries are encoded as JSON strings on Windows. | |
| 183 // | |
| 184 // A dictionary could be stored as a subkey, with each of its entries | |
| 185 // within that subkey. However, it would be impossible to recover the | |
| 186 // type for some of those entries: | |
| 187 // - Booleans are stored as DWORDS and are indistinguishable from | |
| 188 // integers; | |
| 189 // - Lists are stored as a subkey, with entries mapping 0 to N-1 to | |
| 190 // their value. This is indistinguishable from a Dictionary with | |
| 191 // integer keys. | |
| 192 // | |
| 193 // The GPO policy editor also has a limited data entry form that doesn't | |
| 194 // support dictionaries. | |
| 195 RegKey key; | |
| 196 if (!LoadHighestPriorityKey(string16(), name, &key, level, scope)) | |
| 197 return NULL; | |
| 198 string16 value; | |
| 199 if (!ReadRegistryString(&key, name, &value)) | |
| 200 return NULL; | |
| 201 return base::JSONReader::Read(UTF16ToUTF8(value)); | |
| 202 } | |
| 203 | |
| 204 } // namespace | |
| 205 | |
| 206 ConfigurationPolicyProviderDelegateWin::ConfigurationPolicyProviderDelegateWin( | |
| 207 const PolicyDefinitionList* policy_definition_list) | |
| 208 : policy_definition_list_(policy_definition_list) {} | |
| 209 | |
| 210 scoped_ptr<PolicyBundle> ConfigurationPolicyProviderDelegateWin::Load() { | |
| 211 scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); | |
| 212 PolicyMap& chrome_policy = bundle->Get(POLICY_DOMAIN_CHROME, std::string()); | |
| 213 | |
| 214 const PolicyDefinitionList::Entry* current; | |
| 215 for (current = policy_definition_list_->begin; | |
| 216 current != policy_definition_list_->end; | |
| 217 ++current) { | |
| 218 const string16 name(ASCIIToUTF16(current->name)); | |
| 219 PolicyLevel level = POLICY_LEVEL_MANDATORY; | |
| 220 PolicyScope scope = POLICY_SCOPE_MACHINE; | |
| 221 Value* value = NULL; | |
| 222 | |
| 223 switch (current->value_type) { | |
| 224 case Value::TYPE_STRING: | |
| 225 value = ReadStringValue(name, &level, &scope); | |
| 226 break; | |
| 227 | |
| 228 case Value::TYPE_LIST: | |
| 229 value = ReadStringListValue(name, &level, &scope); | |
| 230 break; | |
| 231 | |
| 232 case Value::TYPE_BOOLEAN: | |
| 233 value = ReadBooleanValue(name, &level, &scope); | |
| 234 break; | |
| 235 | |
| 236 case Value::TYPE_INTEGER: | |
| 237 value = ReadIntegerValue(name, &level, &scope); | |
| 238 break; | |
| 239 | |
| 240 case Value::TYPE_DICTIONARY: | |
| 241 value = ReadDictionaryValue(name, &level, &scope); | |
| 242 break; | |
| 243 | |
| 244 default: | |
| 245 NOTREACHED(); | |
| 246 } | |
| 247 | |
| 248 if (value) | |
| 249 chrome_policy.Set(current->name, level, scope, value); | |
| 250 } | |
| 251 | |
| 252 return bundle.Pass(); | |
| 253 } | |
| 254 | |
| 255 } // namespace policy | |
| OLD | NEW |