Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / policy / core / common / preg_parser_win.cc
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_win.h"
6
7 #include <windows.h>
8
9 #include <algorithm>
10 #include <iterator>
11 #include <vector>
12
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"
25
26 namespace policy {
27 namespace preg_parser {
28
29 const char kPRegFileHeader[8] =
30     { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' };
31
32 // Maximum PReg file size we're willing to accept.
33 const int64 kMaxPRegFileSize = 1024 * 1024 * 16;
34
35 // Constants for PReg file delimiters.
36 const base::char16 kDelimBracketOpen = L'[';
37 const base::char16 kDelimBracketClose = L']';
38 const base::char16 kDelimSemicolon = L';';
39
40 // Registry path separator.
41 const base::char16 kRegistryPathSeparator[] = L"\\";
42
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";
51
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;
58
59   int result = **cursor | (*(*cursor + 1) << 8);
60   *cursor += sizeof(base::char16);
61   return result;
62 }
63
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;
71
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;
78 }
79
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;
85   }
86   *data = base::ByteSwapToLE32(value);
87   return true;
88 }
89
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;
97
98   return current == L'\0';
99 }
100
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();
105
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);
112 }
113
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(new base::StringValue(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(new base::FundamentalValue(static_cast<int>(val)));
132         return true;
133       } else {
134         LOG(ERROR) << "Bad data size " << data.size();
135       }
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;
146   }
147
148   return false;
149 }
150
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;
160
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));
171     }
172     dict = subdict;
173   }
174
175   if (value.empty())
176     return;
177
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;
184   }
185
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);
194     }
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);
201     }
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;
214   }
215 }
216
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;
226   }
227
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;
233   }
234
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;
242   }
243
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;
252
253     if (NextChar(&cursor, end) != kDelimBracketOpen)
254       break;
255
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;
262
263     if (!ReadFieldString(&cursor, end, &key_name))
264       break;
265
266     int current = NextChar(&cursor, end);
267     if (current == kDelimSemicolon) {
268       if (!ReadFieldString(&cursor, end, &value))
269         break;
270       current = NextChar(&cursor, end);
271     }
272
273     if (current == kDelimSemicolon) {
274       if (!ReadField32(&cursor, end, &type))
275         break;
276       current = NextChar(&cursor, end);
277     }
278
279     if (current == kDelimSemicolon) {
280       if (!ReadField32(&cursor, end, &size))
281         break;
282       current = NextChar(&cursor, end);
283     }
284
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);
292     }
293
294     if (current != kDelimBracketClose)
295       break;
296
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);
300   }
301
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;
306 }
307
308 }  // namespace preg_parser
309 }  // namespace policy