• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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