Ignore title parameter for navigator.registerProtocolHandler
[chromium-blink-merge.git] / components / policy / core / common / preg_parser_win.cc
blob6c6248ce58cf90a7a328b000f1ccca4dbfccfa3e
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"
7 #include <windows.h>
9 #include <algorithm>
10 #include <iterator>
11 #include <vector>
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"
26 namespace policy {
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)
57 return -1;
59 int result = **cursor | (*(*cursor + 1) << 8);
60 *cursor += sizeof(base::char16);
61 return result;
64 // Reads a fixed-size field from a PReg file.
65 bool ReadFieldBinary(const uint8** cursor,
66 const uint8* end,
67 uint32 size,
68 uint8* data) {
69 if (size == 0)
70 return true;
72 const uint8* field_end = *cursor + size;
73 if (field_end <= *cursor || field_end > end)
74 return false;
75 std::copy(*cursor, field_end, data);
76 *cursor = field_end;
77 return true;
80 bool ReadField32(const uint8** cursor, const uint8* end, uint32* data) {
81 uint32 value = 0;
82 if (!ReadFieldBinary(cursor, end, sizeof(uint32),
83 reinterpret_cast<uint8*>(&value))) {
84 return false;
86 *data = base::ByteSwapToLE32(value);
87 return true;
90 // Reads a string field from a file.
91 bool ReadFieldString(const uint8** cursor,
92 const uint8* end,
93 base::string16* str) {
94 int current = -1;
95 while ((current = NextChar(cursor, end)) > 0x0000)
96 *str += current;
98 return current == L'\0';
101 std::string DecodePRegStringValue(const std::vector<uint8>& data) {
102 size_t len = data.size() / sizeof(base::char16);
103 if (len <= 0)
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) {
118 switch (type) {
119 case REG_SZ:
120 case REG_EXPAND_SZ:
121 value->reset(base::Value::CreateStringValue(DecodePRegStringValue(data)));
122 return true;
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);
129 else
130 val = base::ByteSwapToLE32(val);
131 value->reset(base::Value::CreateIntegerValue(static_cast<int>(val)));
132 return true;
133 } else {
134 LOG(ERROR) << "Bad data size " << data.size();
136 break;
137 case REG_NONE:
138 case REG_LINK:
139 case REG_MULTI_SZ:
140 case REG_RESOURCE_LIST:
141 case REG_FULL_RESOURCE_DESCRIPTOR:
142 case REG_RESOURCE_REQUIREMENTS_LIST:
143 case REG_QWORD_LITTLE_ENDIAN:
144 default:
145 LOG(ERROR) << "Unsupported registry data type " << type;
148 return false;
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,
155 uint32 type,
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) {
164 if (entry->empty())
165 continue;
166 const std::string name = base::UTF16ToUTF8(*entry);
167 RegistryDict* subdict = dict->GetKey(name);
168 if (!subdict) {
169 subdict = new RegistryDict();
170 dict->SetKey(name, make_scoped_ptr(subdict));
172 dict = subdict;
175 if (value.empty())
176 return;
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());
183 return;
186 std::string action_trigger(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)) {
203 dict->RemoveValue(
204 value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
205 arraysize(kActionTriggerDel) - 1));
206 } else if (StartsWithASCII(action_trigger, kActionTriggerDelVals, true)) {
207 // Delete all values.
208 dict->ClearValues();
209 } else if (StartsWithASCII(action_trigger, kActionTriggerSecureKey, true) ||
210 StartsWithASCII(action_trigger, kActionTriggerSoft, true)) {
211 // Doesn't affect values.
212 } else {
213 LOG(ERROR) << "Bad action trigger " << value_name;
217 bool ReadFile(const base::FilePath& file_path,
218 const base::string16& root,
219 RegistryDict* dict,
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);
225 return false;
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);
232 return false;
235 // Check the header.
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);
241 return false;
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();
249 while (true) {
250 if (cursor == end)
251 return true;
253 if (NextChar(&cursor, end) != kDelimBracketOpen)
254 break;
256 // Read the record fields.
257 base::string16 key_name;
258 base::string16 value;
259 uint32 type = 0;
260 uint32 size = 0;
261 std::vector<uint8> data;
263 if (!ReadFieldString(&cursor, end, &key_name))
264 break;
266 int current = NextChar(&cursor, end);
267 if (current == kDelimSemicolon) {
268 if (!ReadFieldString(&cursor, end, &value))
269 break;
270 current = NextChar(&cursor, end);
273 if (current == kDelimSemicolon) {
274 if (!ReadField32(&cursor, end, &type))
275 break;
276 current = NextChar(&cursor, end);
279 if (current == kDelimSemicolon) {
280 if (!ReadField32(&cursor, end, &size))
281 break;
282 current = NextChar(&cursor, end);
285 if (current == kDelimSemicolon) {
286 if (size > kMaxPRegFileSize)
287 break;
288 data.resize(size);
289 if (!ReadFieldBinary(&cursor, end, size, vector_as_array(&data)))
290 break;
291 current = NextChar(&cursor, end);
294 if (current != kDelimBracketClose)
295 break;
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);
305 return false;
308 } // namespace preg_parser
309 } // namespace policy