Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: chrome/browser/policy/preg_parser_win.cc

Issue 13441008: Add parser for PReg files. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments. Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "chrome/browser/policy/preg_parser_win.h"
6
7 #include <windows.h>
8
9 #include "base/basictypes.h"
10 #include "base/files/file_path.h"
11 #include "base/files/memory_mapped_file.h"
12 #include "base/logging.h"
13 #include "base/stl_util.h"
14 #include "base/string16.h"
15 #include "base/string_util.h"
16 #include "base/sys_byteorder.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19
20 namespace policy {
21 namespace preg_parser {
22
23 // The magic header in PReg files: ASCII "PReg" + version (0x0001).
24 const char kPolicyRegistryFileHeader[] = "PReg\x01\x00\x00\x00";
25
26 // Maximum PReg file size we're willing to accept.
27 const int64 kMaxPRegFileSize = 1024 * 1024 * 16;
28
29 // Constants for PReg file delimiters.
30 const char16 kDelimBracketOpen = L'[';
31 const char16 kDelimBracketClose = L']';
32 const char16 kDelimSemicolon = L';';
33
34 // Registry path separator.
35 const char16 kRegistryPathSeparator[] = L"\\";
36
37 // Magic strings for the PReg value field to trigger special actions.
38 const char kActionTriggerPrefix[] = "**";
39 const char kActionTriggerDeleteValues[] = "deletevalues";
40 const char kActionTriggerDel[] = "del.";
41 const char kActionTriggerDelVals[] = "delvals";
42 const char kActionTriggerDeleteKeys[] = "deletekeys";
43 const char kActionTriggerSecureKey[] = "securekey";
44 const char kActionTriggerSoft[] = "soft";
45
46 // Returns the character at |cursor| and increments it, unless the end is here
47 // in which case -1 is returned.
48 int NextChar(const uint8** cursor, const uint8* end) {
49 // Only read the character if a full char16 is available.
50 if (*cursor + sizeof(char16) > end)
51 return -1;
52
53 int result = **cursor | (*(*cursor + 1) << 8);
Joao da Silva 2013/04/08 12:31:25 I suppose the file format always uses LE. But if y
Mattias Nissler (ping if slow) 2013/04/09 22:26:47 We're reading byte-wise here, so this will work al
54 *cursor += sizeof(char16);
55 return result;
56 }
57
58 // Reads a fixed-size field from a PReg file.
59 bool ReadFieldBinary(const uint8** cursor,
60 const uint8* end,
61 int size,
62 uint8* data) {
63 const uint8* field_end = *cursor + size;
64 if (field_end > end)
65 return false;
66 std::copy(*cursor, field_end, data);
67 *cursor = field_end;
68 return true;
69 }
70
71 bool ReadField32(const uint8** cursor, const uint8* end, uint32* data) {
72 uint32 value = 0;
73 if (!ReadFieldBinary(cursor, end, sizeof(uint32),
74 reinterpret_cast<uint8*>(&value))) {
75 return false;
76 }
77 *data = base::ByteSwapToLE32(value);
78 return true;
79 }
80
81 // Reads a string field from a file.
82 bool ReadFieldString(const uint8** cursor, const uint8* end, string16* str) {
83 int current = -1;
84 while ((current = NextChar(cursor, end)) > 0x0000)
85 *str += current;
86
87 return current == L'\0';
88 }
89
90 std::string DecodePRegStringValue(const std::vector<uint8>& data) {
91 size_t len = data.size() / sizeof(char16);
92 if (len <= 0)
93 return std::string();
94
95 const char16* chars = reinterpret_cast<const char16*>(vector_as_array(&data));
96 // Subtract one to account for the trailing null character.
97 return UTF16ToUTF8(string16(chars, len - 1));
98 }
99
100 // Decodes a value from a PReg file given as a uint8 vector.
101 bool DecodePRegValue(uint32 type,
102 const std::vector<uint8>& data,
103 scoped_ptr<base::Value>* value) {
104 switch (type) {
105 case REG_SZ:
106 case REG_EXPAND_SZ:
107 value->reset(base::Value::CreateStringValue(DecodePRegStringValue(data)));
108 return true;
109 case REG_DWORD_LITTLE_ENDIAN:
110 case REG_DWORD_BIG_ENDIAN:
111 if (data.size() == sizeof(uint32)) {
112 uint32 val = *reinterpret_cast<const uint32*>(vector_as_array(&data));
113 if (type == REG_DWORD_BIG_ENDIAN)
114 val = base::NetToHost32(val);
115 else
116 val = base::ByteSwapToLE32(val);
117 value->reset(base::Value::CreateIntegerValue(static_cast<int>(val)));
118 return true;
119 } else {
120 LOG(ERROR) << "Bad data size " << data.size();
121 }
122 break;
123 case REG_NONE:
124 case REG_LINK:
125 case REG_MULTI_SZ:
126 case REG_RESOURCE_LIST:
127 case REG_FULL_RESOURCE_DESCRIPTOR:
128 case REG_RESOURCE_REQUIREMENTS_LIST:
129 case REG_QWORD_LITTLE_ENDIAN:
130 default:
131 LOG(ERROR) << "Unsupported registry data type " << type;
132 }
133
134 return false;
135 }
136
137 // Adds the record data passed via parameters to |dict| in case the data is
138 // relevant policy for Chromium.
139 void HandleRecord(const string16& key_name,
140 const string16& value,
141 uint32 type,
142 const std::vector<uint8>& data,
143 base::DictionaryValue* dict) {
144 // Locate/create the dictionary to place the value in.
145 std::vector<string16> path;
146
147 Tokenize(key_name, kRegistryPathSeparator, &path);
148 for (std::vector<string16>::const_iterator entry(path.begin());
149 entry != path.end(); ++entry) {
150 base::DictionaryValue* subdict = NULL;
151 const std::string name = UTF16ToUTF8(*entry);
152 if (!dict->GetDictionaryWithoutPathExpansion(name, &subdict) ||
153 !subdict) {
154 subdict = new DictionaryValue();
155 dict->SetWithoutPathExpansion(name, subdict);
156 }
157 dict = subdict;
158 }
159
160 if (value.empty())
161 return;
162
163 std::string value_name(UTF16ToUTF8(value));
164 if (!StartsWithASCII(value_name, kActionTriggerPrefix, true)) {
165 scoped_ptr<base::Value> value;
166 if (DecodePRegValue(type, data, &value))
167 dict->Set(value_name, value.release());
168 return;
169 }
170
171 std::string action_trigger(StringToLowerASCII(value_name.substr(
172 arraysize(kActionTriggerPrefix) - 1)));
173 if (action_trigger == kActionTriggerDeleteValues ||
174 StartsWithASCII(action_trigger, kActionTriggerDeleteKeys, true)) {
175 std::vector<std::string> keys;
176 Tokenize(DecodePRegStringValue(data), ";", &keys);
177 for (std::vector<std::string>::const_iterator key(keys.begin());
178 key != keys.end(); ++key) {
179 dict->RemoveWithoutPathExpansion(*key, NULL);
180 }
181 } else if (StartsWithASCII(action_trigger, kActionTriggerDel, true)) {
182 dict->RemoveWithoutPathExpansion(
183 value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
184 arraysize(kActionTriggerDel) - 1),
185 NULL);
186 } else if (StartsWithASCII(action_trigger, kActionTriggerDelVals, true)) {
187 // Delete a values, i.e. keep dictionary entries.
Joao da Silva 2013/04/08 12:31:25 Delete all values
Mattias Nissler (ping if slow) 2013/04/09 22:26:47 Done.
188 base::DictionaryValue new_dict;
189 for (base::DictionaryValue::Iterator it(*dict); it.HasNext();
190 it.Advance()) {
191 base::DictionaryValue* subdict = NULL;
192 if (dict->GetDictionaryWithoutPathExpansion(it.key(), &subdict)) {
193 scoped_ptr<base::DictionaryValue> new_subdict(
194 new base::DictionaryValue());
195 new_subdict->Swap(subdict);
196 new_dict.Set(it.key(), new_subdict.release());
197 }
198 }
199 dict->Swap(&new_dict);
200 } else if (StartsWithASCII(action_trigger, kActionTriggerSecureKey, true) ||
201 StartsWithASCII(action_trigger, kActionTriggerSoft, true)) {
202 // Doesn't affect values.
203 } else {
204 LOG(ERROR) << "Bad action trigger " << value_name;
205 }
206 }
207
208 bool ReadFile(const base::FilePath& file_path,
209 const string16& root,
210 base::DictionaryValue* dict) {
211 base::MemoryMappedFile mapped_file;
212
213 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
214 PLOG(ERROR) << "Failed to map " << file_path.value();
215 return false;
216 }
217
218 if (mapped_file.length() > kMaxPRegFileSize) {
Joao da Silva 2013/04/08 12:31:25 Is 16MB a reasonable size? Let's gather UMA stats
Mattias Nissler (ping if slow) 2013/04/09 22:26:47 We probably want UMA on this, but probably at a di
219 LOG(ERROR) << "PReg file " << file_path.value() << " too large: "
220 << mapped_file.length();
221 return false;
222 }
223
224 // Check the header.
225 const int kHeaderSize = arraysize(kPolicyRegistryFileHeader) - 1;
226 if (mapped_file.length() < kHeaderSize ||
227 memcmp(kPolicyRegistryFileHeader, mapped_file.data(), kHeaderSize) != 0) {
228 LOG(ERROR) << "Bad policy file " << file_path.value();
229 return false;
230 }
231
232 // Parse file contents, which is UCS-2 and little-endian. The latter I
233 // couldn't find documentation on, but the example I saw were all
234 // little-endian. It'd be interesting to check on big-endian hardware.
235 const uint8* cursor = mapped_file.data() + kHeaderSize;
236 const uint8* end = mapped_file.data() + mapped_file.length();
237 while (NextChar(&cursor, end) == kDelimBracketOpen) {
238 // Read the record fields.
239 string16 key_name;
240 string16 value;
241 uint32 type = 0;
242 uint32 size = 0;
243 std::vector<uint8> data;
244
245 if (!ReadFieldString(&cursor, end, &key_name))
246 break;
247
248 int current = NextChar(&cursor, end);
249 if (current == kDelimSemicolon) {
250 if (!ReadFieldString(&cursor, end, &value))
251 break;
252 current = NextChar(&cursor, end);
253 }
254
255 if (current == kDelimSemicolon) {
256 if (!ReadField32(&cursor, end, &type))
257 break;
258 current = NextChar(&cursor, end);
259 }
260
261 if (current == kDelimSemicolon) {
262 if (!ReadField32(&cursor, end, &size))
263 break;
264 current = NextChar(&cursor, end);
265 }
266
267 if (current == kDelimSemicolon) {
268 if (size > kMaxPRegFileSize)
Joao da Silva 2013/04/08 12:31:25 UMA sample?
Mattias Nissler (ping if slow) 2013/04/09 22:26:47 See above, the error return is good enough for now
269 break;
270 data.resize(size);
271 if (!ReadFieldBinary(&cursor, end, size, vector_as_array(&data)))
272 break;
273 current = NextChar(&cursor, end);
274 }
275
276 if (current != kDelimBracketClose)
277 break;
278
279 // Process the record if it's for |root| or below.
280 if (StartsWith(key_name, root, true) &&
Joao da Silva 2013/04/08 12:31:25 Registry keys and values are case-insensitive, sho
Mattias Nissler (ping if slow) 2013/04/09 22:26:47 Done.
281 (root.empty() ||
282 key_name.size() == root.size() ||
283 key_name[root.size()] == kRegistryPathSeparator[0])) {
284 HandleRecord(key_name, value, type, data, dict);
285 }
286
287 if (cursor == end)
288 return true;
289 }
290
291 LOG(ERROR) << "Error parsing " << file_path.value() << " at offset "
292 << reinterpret_cast<const uint8*>(cursor - 1) - mapped_file.data();
293 return false;
294 }
295
296 } // namespace preg_parser
297 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/policy/preg_parser_win.h ('k') | chrome/browser/policy/preg_parser_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698