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.
4
5 #include "components/policy/core/common/preg_parser.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <iterator>
12 #include <limits>
13 #include <memory>
14 #include <string>
15 #include <utility>
16 #include <vector>
17
18 #include "base/files/file_path.h"
19 #include "base/files/memory_mapped_file.h"
20 #include "base/logging.h"
21 #include "base/macros.h"
22 #include "base/memory/ptr_util.h"
23 #include "base/strings/string16.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/sys_byteorder.h"
29 #include "base/values.h"
30 #include "components/policy/core/common/registry_dict.h"
31
32 #if defined(OS_WIN)
33 #include <windows.h>
34 #else
35 // Registry data type constants.
36 #define REG_NONE 0
37 #define REG_SZ 1
38 #define REG_EXPAND_SZ 2
39 #define REG_BINARY 3
40 #define REG_DWORD_LITTLE_ENDIAN 4
41 #define REG_DWORD_BIG_ENDIAN 5
42 #define REG_LINK 6
43 #define REG_MULTI_SZ 7
44 #define REG_RESOURCE_LIST 8
45 #define REG_FULL_RESOURCE_DESCRIPTOR 9
46 #define REG_RESOURCE_REQUIREMENTS_LIST 10
47 #define REG_QWORD_LITTLE_ENDIAN 11
48 #endif
49
50 using RegistryDict = policy::RegistryDict;
51
52 namespace {
53
54 // Maximum PReg file size we're willing to accept.
55 const int64_t kMaxPRegFileSize = 1024 * 1024 * 16;
56 static_assert(kMaxPRegFileSize <= std::numeric_limits<ptrdiff_t>::max(),
57 "Max PReg file size too large.");
58
59 // Maximum number of components in registry key names. This corresponds to the
60 // maximum nesting level of RegistryDict trees.
61 const size_t kMaxKeyNameComponents = 1024;
62
63 // Constants for PReg file delimiters.
64 const base::char16 kDelimBracketOpen = L'[';
65 const base::char16 kDelimBracketClose = L']';
66 const base::char16 kDelimSemicolon = L';';
67
68 // Registry path separator.
69 const base::char16 kRegistryPathSeparator[] = {L'\\', L'\0'};
70
71 // Magic strings for the PReg value field to trigger special actions.
72 const char kActionTriggerPrefix[] = "**";
73 const char kActionTriggerDeleteValues[] = "deletevalues";
74 const char kActionTriggerDel[] = "del.";
75 const char kActionTriggerDelVals[] = "delvals";
76 const char kActionTriggerDeleteKeys[] = "deletekeys";
77 const char kActionTriggerSecureKey[] = "securekey";
78 const char kActionTriggerSoft[] = "soft";
79
80 // Returns the character at |cursor| and increments it, unless the end is here
81 // in which case -1 is returned. The calling code must guarantee that
82 // end - *cursor does not overflow ptrdiff_t.
NextChar(const uint8_t ** cursor,const uint8_t * end)83 int NextChar(const uint8_t** cursor, const uint8_t* end) {
84 // Only read the character if a full base::char16 is available.
85 // This comparison makes sure no overflow can happen.
86 if (*cursor >= end ||
87 end - *cursor < static_cast<ptrdiff_t>(sizeof(base::char16)))
88 return -1;
89
90 int result = **cursor | (*(*cursor + 1) << 8);
91 *cursor += sizeof(base::char16);
92 return result;
93 }
94
95 // Reads a fixed-size field from a PReg file. The calling code must guarantee
96 // that both end - *cursor and size do not overflow ptrdiff_t.
ReadFieldBinary(const uint8_t ** cursor,const uint8_t * end,uint32_t size,uint8_t * data)97 bool ReadFieldBinary(const uint8_t** cursor,
98 const uint8_t* end,
99 uint32_t size,
100 uint8_t* data) {
101 if (size == 0)
102 return true;
103
104 // Be careful to prevent possible overflows here (don't do *cursor + size).
105 if (*cursor >= end || end - *cursor < static_cast<ptrdiff_t>(size))
106 return false;
107 const uint8_t* field_end = *cursor + size;
108 std::copy(*cursor, field_end, data);
109 *cursor = field_end;
110 return true;
111 }
112
ReadField32(const uint8_t ** cursor,const uint8_t * end,uint32_t * data)113 bool ReadField32(const uint8_t** cursor, const uint8_t* end, uint32_t* data) {
114 uint32_t value = 0;
115 if (!ReadFieldBinary(cursor, end, sizeof(uint32_t),
116 reinterpret_cast<uint8_t*>(&value))) {
117 return false;
118 }
119 *data = base::ByteSwapToLE32(value);
120 return true;
121 }
122
123 // Reads a string field from a file.
ReadFieldString(const uint8_t ** cursor,const uint8_t * end,base::string16 * str)124 bool ReadFieldString(const uint8_t** cursor,
125 const uint8_t* end,
126 base::string16* str) {
127 int current = -1;
128 while ((current = NextChar(cursor, end)) > 0x0000)
129 *str += current;
130
131 return current == L'\0';
132 }
133
134 // Converts the UTF16 |data| to an UTF8 string |value|. Returns false if the
135 // resulting UTF8 string contains invalid characters.
DecodePRegStringValue(const std::vector<uint8_t> & data,std::string * value)136 bool DecodePRegStringValue(const std::vector<uint8_t>& data,
137 std::string* value) {
138 size_t len = data.size() / sizeof(base::char16);
139 if (len <= 0) {
140 value->clear();
141 return true;
142 }
143
144 const base::char16* chars =
145 reinterpret_cast<const base::char16*>(data.data());
146 base::string16 utf16_str;
147 std::transform(chars, chars + len - 1, std::back_inserter(utf16_str),
148 base::ByteSwapToLE16);
149 // Note: UTF16ToUTF8() only checks whether all chars are valid code points,
150 // but not whether they're valid characters. IsStringUTF8(), however, does.
151 *value = base::UTF16ToUTF8(utf16_str);
152 if (!base::IsStringUTF8(*value)) {
153 LOG(ERROR) << "String '" << *value << "' is not a valid UTF8 string";
154 value->clear();
155 return false;
156 }
157 return true;
158 }
159
160 // Decodes a value from a PReg file given as a uint8_t vector.
DecodePRegValue(uint32_t type,const std::vector<uint8_t> & data,std::unique_ptr<base::Value> * value)161 bool DecodePRegValue(uint32_t type,
162 const std::vector<uint8_t>& data,
163 std::unique_ptr<base::Value>* value) {
164 std::string data_utf8;
165 switch (type) {
166 case REG_SZ:
167 case REG_EXPAND_SZ:
168 if (!DecodePRegStringValue(data, &data_utf8))
169 return false;
170 value->reset(new base::Value(data_utf8));
171 return true;
172 case REG_DWORD_LITTLE_ENDIAN:
173 case REG_DWORD_BIG_ENDIAN:
174 if (data.size() == sizeof(uint32_t)) {
175 uint32_t val = *reinterpret_cast<const uint32_t*>(data.data());
176 if (type == REG_DWORD_BIG_ENDIAN)
177 val = base::NetToHost32(val);
178 else
179 val = base::ByteSwapToLE32(val);
180 value->reset(new base::Value(static_cast<int>(val)));
181 return true;
182 } else {
183 LOG(ERROR) << "Bad data size " << data.size();
184 }
185 break;
186 case REG_NONE:
187 case REG_LINK:
188 case REG_MULTI_SZ:
189 case REG_RESOURCE_LIST:
190 case REG_FULL_RESOURCE_DESCRIPTOR:
191 case REG_RESOURCE_REQUIREMENTS_LIST:
192 case REG_QWORD_LITTLE_ENDIAN:
193 default:
194 LOG(ERROR) << "Unsupported registry data type " << type;
195 }
196
197 return false;
198 }
199
200 // Returns true if the registry key |key_name| belongs to the sub-tree specified
201 // by the key |root|.
KeyRootEquals(const base::string16 & key_name,const base::string16 & root)202 bool KeyRootEquals(const base::string16& key_name, const base::string16& root) {
203 if (root.empty())
204 return true;
205
206 if (!base::StartsWith(key_name, root, base::CompareCase::INSENSITIVE_ASCII))
207 return false;
208
209 // Handle the case where |root| == "ABC" and |key_name| == "ABCDE\FG". This
210 // should not be interpreted as a match.
211 return key_name.length() == root.length() ||
212 key_name.at(root.length()) == kRegistryPathSeparator[0];
213 }
214
215 // Adds |value| and |data| to |dict| or an appropriate sub-dictionary indicated
216 // by |key_name|. Creates sub-dictionaries if necessary. Also handles special
217 // action triggers, see |kActionTrigger*|, that can, for instance, remove an
218 // existing value.
HandleRecord(const base::string16 & key_name,const base::string16 & value,uint32_t type,const std::vector<uint8_t> & data,RegistryDict * dict)219 void HandleRecord(const base::string16& key_name,
220 const base::string16& value,
221 uint32_t type,
222 const std::vector<uint8_t>& data,
223 RegistryDict* dict) {
224 // Locate/create the dictionary to place the value in.
225 std::vector<base::string16> path;
226
227 std::vector<base::StringPiece16> key_name_components =
228 base::SplitStringPiece(key_name, kRegistryPathSeparator,
229 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
230 if (key_name_components.size() > kMaxKeyNameComponents) {
231 LOG(ERROR) << "Encountered a key which has more than "
232 << kMaxKeyNameComponents << " components.";
233 return;
234 }
235 for (const base::StringPiece16& key_name_component : key_name_components) {
236 if (key_name_component.empty())
237 continue;
238
239 const std::string name = base::UTF16ToUTF8(key_name_component);
240 RegistryDict* subdict = dict->GetKey(name);
241 if (!subdict) {
242 subdict = new RegistryDict();
243 dict->SetKey(name, base::WrapUnique(subdict));
244 }
245 dict = subdict;
246 }
247
248 if (value.empty())
249 return;
250
251 std::string value_name(base::UTF16ToUTF8(value));
252 if (!base::StartsWith(value_name, kActionTriggerPrefix,
253 base::CompareCase::SENSITIVE)) {
254 std::unique_ptr<base::Value> value;
255 if (DecodePRegValue(type, data, &value))
256 dict->SetValue(value_name, std::move(value));
257 return;
258 }
259
260 std::string data_utf8;
261 std::string action_trigger(base::ToLowerASCII(
262 value_name.substr(arraysize(kActionTriggerPrefix) - 1)));
263 if (action_trigger == kActionTriggerDeleteValues) {
264 if (DecodePRegStringValue(data, &data_utf8)) {
265 for (const std::string& value :
266 base::SplitString(data_utf8, ";", base::KEEP_WHITESPACE,
267 base::SPLIT_WANT_NONEMPTY))
268 dict->RemoveValue(value);
269 }
270 } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys,
271 base::CompareCase::SENSITIVE)) {
272 if (DecodePRegStringValue(data, &data_utf8)) {
273 for (const std::string& key :
274 base::SplitString(data_utf8, ";", base::KEEP_WHITESPACE,
275 base::SPLIT_WANT_NONEMPTY))
276 dict->RemoveKey(key);
277 }
278 } else if (base::StartsWith(action_trigger, kActionTriggerDel,
279 base::CompareCase::SENSITIVE)) {
280 dict->RemoveValue(value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
281 arraysize(kActionTriggerDel) - 1));
282 } else if (base::StartsWith(action_trigger, kActionTriggerDelVals,
283 base::CompareCase::SENSITIVE)) {
284 // Delete all values.
285 dict->ClearValues();
286 } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey,
287 base::CompareCase::SENSITIVE) ||
288 base::StartsWith(action_trigger, kActionTriggerSoft,
289 base::CompareCase::SENSITIVE)) {
290 // Doesn't affect values.
291 } else {
292 LOG(ERROR) << "Bad action trigger " << value_name;
293 }
294 }
295
296 } // namespace
297
298 namespace policy {
299 namespace preg_parser {
300
301 const char kPRegFileHeader[8] = {'P', 'R', 'e', 'g',
302 '\x01', '\x00', '\x00', '\x00'};
303
ReadFile(const base::FilePath & file_path,const base::string16 & root,RegistryDict * dict,PolicyLoadStatusSampler * status)304 bool ReadFile(const base::FilePath& file_path,
305 const base::string16& root,
306 RegistryDict* dict,
307 PolicyLoadStatusSampler* status) {
308 base::MemoryMappedFile mapped_file;
309 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
310 PLOG(ERROR) << "Failed to map " << file_path.value();
311 status->Add(POLICY_LOAD_STATUS_READ_ERROR);
312 return false;
313 }
314
315 return ReadDataInternal(
316 mapped_file.data(), mapped_file.length(), root, dict, status,
317 base::StringPrintf("file '%" PRIsFP "'", file_path.value().c_str()));
318 }
319
ReadDataInternal(const uint8_t * preg_data,size_t preg_data_size,const base::string16 & root,RegistryDict * dict,PolicyLoadStatusSampler * status,const std::string & debug_name)320 POLICY_EXPORT bool ReadDataInternal(const uint8_t* preg_data,
321 size_t preg_data_size,
322 const base::string16& root,
323 RegistryDict* dict,
324 PolicyLoadStatusSampler* status,
325 const std::string& debug_name) {
326 DCHECK(status);
327 DCHECK(root.empty() || root.back() != kRegistryPathSeparator[0]);
328
329 // Check data size.
330 if (preg_data_size > kMaxPRegFileSize) {
331 LOG(ERROR) << "PReg " << debug_name << " too large: " << preg_data_size;
332 status->Add(POLICY_LOAD_STATUS_TOO_BIG);
333 return false;
334 }
335
336 // Check the header.
337 const int kHeaderSize = arraysize(kPRegFileHeader);
338 if (!preg_data || preg_data_size < kHeaderSize ||
339 memcmp(kPRegFileHeader, preg_data, kHeaderSize) != 0) {
340 LOG(ERROR) << "Bad PReg " << debug_name;
341 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
342 return false;
343 }
344
345 // Parse data, which is expected to be UCS-2 and little-endian. The latter I
346 // couldn't find documentation on, but the example I saw were all
347 // little-endian. It'd be interesting to check on big-endian hardware.
348 const uint8_t* cursor = preg_data + kHeaderSize;
349 const uint8_t* end = preg_data + preg_data_size;
350 while (true) {
351 if (cursor == end)
352 return true;
353
354 if (NextChar(&cursor, end) != kDelimBracketOpen)
355 break;
356
357 // Read the record fields.
358 base::string16 key_name;
359 base::string16 value;
360 uint32_t type = 0;
361 uint32_t size = 0;
362 std::vector<uint8_t> data;
363
364 if (!ReadFieldString(&cursor, end, &key_name))
365 break;
366
367 int current = NextChar(&cursor, end);
368 if (current == kDelimSemicolon) {
369 if (!ReadFieldString(&cursor, end, &value))
370 break;
371 current = NextChar(&cursor, end);
372 }
373
374 if (current == kDelimSemicolon) {
375 if (!ReadField32(&cursor, end, &type))
376 break;
377 current = NextChar(&cursor, end);
378 }
379
380 if (current == kDelimSemicolon) {
381 if (!ReadField32(&cursor, end, &size))
382 break;
383 current = NextChar(&cursor, end);
384 }
385
386 if (current == kDelimSemicolon) {
387 if (size > kMaxPRegFileSize)
388 break;
389 data.resize(size);
390 if (!ReadFieldBinary(&cursor, end, size, data.data()))
391 break;
392 current = NextChar(&cursor, end);
393 }
394
395 if (current != kDelimBracketClose)
396 break;
397
398 // Process the record if it is within the |root| subtree.
399 if (KeyRootEquals(key_name, root))
400 HandleRecord(key_name.substr(root.size()), value, type, data, dict);
401 }
402
403 LOG(ERROR) << "Error parsing PReg " << debug_name << " at offset "
404 << (reinterpret_cast<const uint8_t*>(cursor - 1) - preg_data);
405 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
406 return false;
407 }
408
409 } // namespace preg_parser
410 } // namespace policy
411