1 // Copyright (c) 2013 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.
5 #include "components/policy/core/common/preg_parser_win.h"
13 #include "base/basictypes.h"
14 #include "base/files/file_path.h"
15 #include "base/files/memory_mapped_file.h"
16 #include "base/logging.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/sys_byteorder.h"
22 #include "base/values.h"
23 #include "components/policy/core/common/policy_load_status.h"
24 #include "components/policy/core/common/registry_dict_win.h"
27 namespace preg_parser {
29 const char kPRegFileHeader[8] =
30 { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' };
32 // Maximum PReg file size we're willing to accept.
33 const int64 kMaxPRegFileSize = 1024 * 1024 * 16;
35 // Constants for PReg file delimiters.
36 const base::char16 kDelimBracketOpen = L'[';
37 const base::char16 kDelimBracketClose = L']';
38 const base::char16 kDelimSemicolon = L';';
40 // Registry path separator.
41 const base::char16 kRegistryPathSeparator[] = L"\\";
43 // Magic strings for the PReg value field to trigger special actions.
44 const char kActionTriggerPrefix[] = "**";
45 const char kActionTriggerDeleteValues[] = "deletevalues";
46 const char kActionTriggerDel[] = "del.";
47 const char kActionTriggerDelVals[] = "delvals";
48 const char kActionTriggerDeleteKeys[] = "deletekeys";
49 const char kActionTriggerSecureKey[] = "securekey";
50 const char kActionTriggerSoft[] = "soft";
52 // Returns the character at |cursor| and increments it, unless the end is here
53 // in which case -1 is returned.
54 int NextChar(const uint8** cursor, const uint8* end) {
55 // Only read the character if a full base::char16 is available.
56 if (*cursor + sizeof(base::char16) > end)
59 int result = **cursor | (*(*cursor + 1) << 8);
60 *cursor += sizeof(base::char16);
64 // Reads a fixed-size field from a PReg file.
65 bool ReadFieldBinary(const uint8** cursor,
72 const uint8* field_end = *cursor + size;
73 if (field_end <= *cursor || field_end > end)
75 std::copy(*cursor, field_end, data);
80 bool ReadField32(const uint8** cursor, const uint8* end, uint32* data) {
82 if (!ReadFieldBinary(cursor, end, sizeof(uint32),
83 reinterpret_cast<uint8*>(&value))) {
86 *data = base::ByteSwapToLE32(value);
90 // Reads a string field from a file.
91 bool ReadFieldString(const uint8** cursor,
93 base::string16* str) {
95 while ((current = NextChar(cursor, end)) > 0x0000)
98 return current == L'\0';
101 std::string DecodePRegStringValue(const std::vector<uint8>& data) {
102 size_t len = data.size() / sizeof(base::char16);
104 return std::string();
106 const base::char16* chars =
107 reinterpret_cast<const base::char16*>(vector_as_array(&data));
108 base::string16 result;
109 std::transform(chars, chars + len - 1, std::back_inserter(result),
110 std::ptr_fun(base::ByteSwapToLE16));
111 return base::UTF16ToUTF8(result);
114 // Decodes a value from a PReg file given as a uint8 vector.
115 bool DecodePRegValue(uint32 type,
116 const std::vector<uint8>& data,
117 scoped_ptr<base::Value>* value) {
121 value->reset(new base::StringValue(DecodePRegStringValue(data)));
123 case REG_DWORD_LITTLE_ENDIAN:
124 case REG_DWORD_BIG_ENDIAN:
125 if (data.size() == sizeof(uint32)) {
126 uint32 val = *reinterpret_cast<const uint32*>(vector_as_array(&data));
127 if (type == REG_DWORD_BIG_ENDIAN)
128 val = base::NetToHost32(val);
130 val = base::ByteSwapToLE32(val);
131 value->reset(new base::FundamentalValue(static_cast<int>(val)));
134 LOG(ERROR) << "Bad data size " << data.size();
140 case REG_RESOURCE_LIST:
141 case REG_FULL_RESOURCE_DESCRIPTOR:
142 case REG_RESOURCE_REQUIREMENTS_LIST:
143 case REG_QWORD_LITTLE_ENDIAN:
145 LOG(ERROR) << "Unsupported registry data type " << type;
151 // Adds the record data passed via parameters to |dict| in case the data is
152 // relevant policy for Chromium.
153 void HandleRecord(const base::string16& key_name,
154 const base::string16& value,
156 const std::vector<uint8>& data,
157 RegistryDict* dict) {
158 // Locate/create the dictionary to place the value in.
159 std::vector<base::string16> path;
161 Tokenize(key_name, kRegistryPathSeparator, &path);
162 for (std::vector<base::string16>::const_iterator entry(path.begin());
163 entry != path.end(); ++entry) {
166 const std::string name = base::UTF16ToUTF8(*entry);
167 RegistryDict* subdict = dict->GetKey(name);
169 subdict = new RegistryDict();
170 dict->SetKey(name, make_scoped_ptr(subdict));
178 std::string value_name(base::UTF16ToUTF8(value));
179 if (!StartsWithASCII(value_name, kActionTriggerPrefix, true)) {
180 scoped_ptr<base::Value> value;
181 if (DecodePRegValue(type, data, &value))
182 dict->SetValue(value_name, value.Pass());
186 std::string action_trigger(base::StringToLowerASCII(value_name.substr(
187 arraysize(kActionTriggerPrefix) - 1)));
188 if (action_trigger == kActionTriggerDeleteValues) {
189 std::vector<std::string> values;
190 Tokenize(DecodePRegStringValue(data), ";", &values);
191 for (std::vector<std::string>::const_iterator value(values.begin());
192 value != values.end(); ++value) {
193 dict->RemoveValue(*value);
195 } else if (StartsWithASCII(action_trigger, kActionTriggerDeleteKeys, true)) {
196 std::vector<std::string> keys;
197 Tokenize(DecodePRegStringValue(data), ";", &keys);
198 for (std::vector<std::string>::const_iterator key(keys.begin());
199 key != keys.end(); ++key) {
200 dict->RemoveKey(*key);
202 } else if (StartsWithASCII(action_trigger, kActionTriggerDel, true)) {
204 value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
205 arraysize(kActionTriggerDel) - 1));
206 } else if (StartsWithASCII(action_trigger, kActionTriggerDelVals, true)) {
207 // Delete all values.
209 } else if (StartsWithASCII(action_trigger, kActionTriggerSecureKey, true) ||
210 StartsWithASCII(action_trigger, kActionTriggerSoft, true)) {
211 // Doesn't affect values.
213 LOG(ERROR) << "Bad action trigger " << value_name;
217 bool ReadFile(const base::FilePath& file_path,
218 const base::string16& root,
220 PolicyLoadStatusSample* status) {
221 base::MemoryMappedFile mapped_file;
222 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
223 PLOG(ERROR) << "Failed to map " << file_path.value();
224 status->Add(POLICY_LOAD_STATUS_READ_ERROR);
228 if (mapped_file.length() > kMaxPRegFileSize) {
229 LOG(ERROR) << "PReg file " << file_path.value() << " too large: "
230 << mapped_file.length();
231 status->Add(POLICY_LOAD_STATUS_TOO_BIG);
236 const int kHeaderSize = arraysize(kPRegFileHeader);
237 if (mapped_file.length() < kHeaderSize ||
238 memcmp(kPRegFileHeader, mapped_file.data(), kHeaderSize) != 0) {
239 LOG(ERROR) << "Bad policy file " << file_path.value();
240 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
244 // Parse file contents, which is UCS-2 and little-endian. The latter I
245 // couldn't find documentation on, but the example I saw were all
246 // little-endian. It'd be interesting to check on big-endian hardware.
247 const uint8* cursor = mapped_file.data() + kHeaderSize;
248 const uint8* end = mapped_file.data() + mapped_file.length();
253 if (NextChar(&cursor, end) != kDelimBracketOpen)
256 // Read the record fields.
257 base::string16 key_name;
258 base::string16 value;
261 std::vector<uint8> data;
263 if (!ReadFieldString(&cursor, end, &key_name))
266 int current = NextChar(&cursor, end);
267 if (current == kDelimSemicolon) {
268 if (!ReadFieldString(&cursor, end, &value))
270 current = NextChar(&cursor, end);
273 if (current == kDelimSemicolon) {
274 if (!ReadField32(&cursor, end, &type))
276 current = NextChar(&cursor, end);
279 if (current == kDelimSemicolon) {
280 if (!ReadField32(&cursor, end, &size))
282 current = NextChar(&cursor, end);
285 if (current == kDelimSemicolon) {
286 if (size > kMaxPRegFileSize)
289 if (!ReadFieldBinary(&cursor, end, size, vector_as_array(&data)))
291 current = NextChar(&cursor, end);
294 if (current != kDelimBracketClose)
297 // Process the record if it is within the |root| subtree.
298 if (StartsWith(key_name, root, false))
299 HandleRecord(key_name.substr(root.size()), value, type, data, dict);
302 LOG(ERROR) << "Error parsing " << file_path.value() << " at offset "
303 << reinterpret_cast<const uint8*>(cursor - 1) - mapped_file.data();
304 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
308 } // namespace preg_parser
309 } // namespace policy