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

Side by Side Diff: components/policy/core/common/preg_parser_win.cc

Issue 2481753002: Push preg_parser and registry_dict changes upstream (Closed)
Patch Set: Minor formatting fixes Created 4 years, 1 month 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
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 "components/policy/core/common/preg_parser_win.h"
6
7 #include <windows.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include <algorithm>
12 #include <functional>
13 #include <iterator>
14 #include <utility>
15 #include <vector>
16
17 #include "base/files/file_path.h"
18 #include "base/files/memory_mapped_file.h"
19 #include "base/i18n/case_conversion.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/utf_string_conversions.h"
27 #include "base/sys_byteorder.h"
28 #include "base/values.h"
29 #include "components/policy/core/common/policy_load_status.h"
30 #include "components/policy/core/common/registry_dict_win.h"
31
32 namespace policy {
33 namespace preg_parser {
34
35 const char kPRegFileHeader[8] =
36 { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' };
37
38 // Maximum PReg file size we're willing to accept.
39 const int64_t kMaxPRegFileSize = 1024 * 1024 * 16;
40
41 // Constants for PReg file delimiters.
42 const base::char16 kDelimBracketOpen = L'[';
43 const base::char16 kDelimBracketClose = L']';
44 const base::char16 kDelimSemicolon = L';';
45
46 // Registry path separator.
47 const base::char16 kRegistryPathSeparator[] = L"\\";
48
49 // Magic strings for the PReg value field to trigger special actions.
50 const char kActionTriggerPrefix[] = "**";
51 const char kActionTriggerDeleteValues[] = "deletevalues";
52 const char kActionTriggerDel[] = "del.";
53 const char kActionTriggerDelVals[] = "delvals";
54 const char kActionTriggerDeleteKeys[] = "deletekeys";
55 const char kActionTriggerSecureKey[] = "securekey";
56 const char kActionTriggerSoft[] = "soft";
57
58 // Returns the character at |cursor| and increments it, unless the end is here
59 // in which case -1 is returned.
60 int NextChar(const uint8_t** cursor, const uint8_t* end) {
61 // Only read the character if a full base::char16 is available.
62 if (*cursor + sizeof(base::char16) > end)
63 return -1;
64
65 int result = **cursor | (*(*cursor + 1) << 8);
66 *cursor += sizeof(base::char16);
67 return result;
68 }
69
70 // Reads a fixed-size field from a PReg file.
71 bool ReadFieldBinary(const uint8_t** cursor,
72 const uint8_t* end,
73 uint32_t size,
74 uint8_t* data) {
75 if (size == 0)
76 return true;
77
78 const uint8_t* field_end = *cursor + size;
79 if (field_end <= *cursor || field_end > end)
80 return false;
81 std::copy(*cursor, field_end, data);
82 *cursor = field_end;
83 return true;
84 }
85
86 bool ReadField32(const uint8_t** cursor, const uint8_t* end, uint32_t* data) {
87 uint32_t value = 0;
88 if (!ReadFieldBinary(cursor, end, sizeof(uint32_t),
89 reinterpret_cast<uint8_t*>(&value))) {
90 return false;
91 }
92 *data = base::ByteSwapToLE32(value);
93 return true;
94 }
95
96 // Reads a string field from a file.
97 bool ReadFieldString(const uint8_t** cursor,
98 const uint8_t* end,
99 base::string16* str) {
100 int current = -1;
101 while ((current = NextChar(cursor, end)) > 0x0000)
102 *str += current;
103
104 return current == L'\0';
105 }
106
107 std::string DecodePRegStringValue(const std::vector<uint8_t>& data) {
108 size_t len = data.size() / sizeof(base::char16);
109 if (len <= 0)
110 return std::string();
111
112 const base::char16* chars =
113 reinterpret_cast<const base::char16*>(data.data());
114 base::string16 result;
115 std::transform(chars, chars + len - 1, std::back_inserter(result),
116 std::ptr_fun(base::ByteSwapToLE16));
117 return base::UTF16ToUTF8(result);
118 }
119
120 // Decodes a value from a PReg file given as a uint8_t vector.
121 bool DecodePRegValue(uint32_t type,
122 const std::vector<uint8_t>& data,
123 std::unique_ptr<base::Value>* value) {
124 switch (type) {
125 case REG_SZ:
126 case REG_EXPAND_SZ:
127 value->reset(new base::StringValue(DecodePRegStringValue(data)));
128 return true;
129 case REG_DWORD_LITTLE_ENDIAN:
130 case REG_DWORD_BIG_ENDIAN:
131 if (data.size() == sizeof(uint32_t)) {
132 uint32_t val = *reinterpret_cast<const uint32_t*>(data.data());
133 if (type == REG_DWORD_BIG_ENDIAN)
134 val = base::NetToHost32(val);
135 else
136 val = base::ByteSwapToLE32(val);
137 value->reset(new base::FundamentalValue(static_cast<int>(val)));
138 return true;
139 } else {
140 LOG(ERROR) << "Bad data size " << data.size();
141 }
142 break;
143 case REG_NONE:
144 case REG_LINK:
145 case REG_MULTI_SZ:
146 case REG_RESOURCE_LIST:
147 case REG_FULL_RESOURCE_DESCRIPTOR:
148 case REG_RESOURCE_REQUIREMENTS_LIST:
149 case REG_QWORD_LITTLE_ENDIAN:
150 default:
151 LOG(ERROR) << "Unsupported registry data type " << type;
152 }
153
154 return false;
155 }
156
157 // Adds the record data passed via parameters to |dict| in case the data is
158 // relevant policy for Chromium.
159 void HandleRecord(const base::string16& key_name,
160 const base::string16& value,
161 uint32_t type,
162 const std::vector<uint8_t>& data,
163 RegistryDict* dict) {
164 // Locate/create the dictionary to place the value in.
165 std::vector<base::string16> path;
166
167 for (const base::string16& entry :
168 base::SplitString(key_name, kRegistryPathSeparator,
169 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
170 if (entry.empty())
171 continue;
172 const std::string name = base::UTF16ToUTF8(entry);
173 RegistryDict* subdict = dict->GetKey(name);
174 if (!subdict) {
175 subdict = new RegistryDict();
176 dict->SetKey(name, base::WrapUnique(subdict));
177 }
178 dict = subdict;
179 }
180
181 if (value.empty())
182 return;
183
184 std::string value_name(base::UTF16ToUTF8(value));
185 if (!base::StartsWith(value_name, kActionTriggerPrefix,
186 base::CompareCase::SENSITIVE)) {
187 std::unique_ptr<base::Value> value;
188 if (DecodePRegValue(type, data, &value))
189 dict->SetValue(value_name, std::move(value));
190 return;
191 }
192
193 std::string action_trigger(base::ToLowerASCII(value_name.substr(
194 arraysize(kActionTriggerPrefix) - 1)));
195 if (action_trigger == kActionTriggerDeleteValues) {
196 for (const std::string& value :
197 base::SplitString(DecodePRegStringValue(data), ";",
198 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY))
199 dict->RemoveValue(value);
200 } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys,
201 base::CompareCase::SENSITIVE)) {
202 for (const std::string& key :
203 base::SplitString(DecodePRegStringValue(data), ";",
204 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY))
205 dict->RemoveKey(key);
206 } else if (base::StartsWith(action_trigger, kActionTriggerDel,
207 base::CompareCase::SENSITIVE)) {
208 dict->RemoveValue(
209 value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
210 arraysize(kActionTriggerDel) - 1));
211 } else if (base::StartsWith(action_trigger, kActionTriggerDelVals,
212 base::CompareCase::SENSITIVE)) {
213 // Delete all values.
214 dict->ClearValues();
215 } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey,
216 base::CompareCase::SENSITIVE) ||
217 base::StartsWith(action_trigger, kActionTriggerSoft,
218 base::CompareCase::SENSITIVE)) {
219 // Doesn't affect values.
220 } else {
221 LOG(ERROR) << "Bad action trigger " << value_name;
222 }
223 }
224
225 bool ReadFile(const base::FilePath& file_path,
226 const base::string16& root,
227 RegistryDict* dict,
228 PolicyLoadStatusSample* status) {
229 base::MemoryMappedFile mapped_file;
230 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
231 PLOG(ERROR) << "Failed to map " << file_path.value();
232 status->Add(POLICY_LOAD_STATUS_READ_ERROR);
233 return false;
234 }
235
236 if (mapped_file.length() > kMaxPRegFileSize) {
237 LOG(ERROR) << "PReg file " << file_path.value() << " too large: "
238 << mapped_file.length();
239 status->Add(POLICY_LOAD_STATUS_TOO_BIG);
240 return false;
241 }
242
243 // Check the header.
244 const int kHeaderSize = arraysize(kPRegFileHeader);
245 if (mapped_file.length() < kHeaderSize ||
246 memcmp(kPRegFileHeader, mapped_file.data(), kHeaderSize) != 0) {
247 LOG(ERROR) << "Bad policy file " << file_path.value();
248 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
249 return false;
250 }
251
252 // Parse file contents, which is UCS-2 and little-endian. The latter I
253 // couldn't find documentation on, but the example I saw were all
254 // little-endian. It'd be interesting to check on big-endian hardware.
255 const uint8_t* cursor = mapped_file.data() + kHeaderSize;
256 const uint8_t* end = mapped_file.data() + mapped_file.length();
257 while (true) {
258 if (cursor == end)
259 return true;
260
261 if (NextChar(&cursor, end) != kDelimBracketOpen)
262 break;
263
264 // Read the record fields.
265 base::string16 key_name;
266 base::string16 value;
267 uint32_t type = 0;
268 uint32_t size = 0;
269 std::vector<uint8_t> data;
270
271 if (!ReadFieldString(&cursor, end, &key_name))
272 break;
273
274 int current = NextChar(&cursor, end);
275 if (current == kDelimSemicolon) {
276 if (!ReadFieldString(&cursor, end, &value))
277 break;
278 current = NextChar(&cursor, end);
279 }
280
281 if (current == kDelimSemicolon) {
282 if (!ReadField32(&cursor, end, &type))
283 break;
284 current = NextChar(&cursor, end);
285 }
286
287 if (current == kDelimSemicolon) {
288 if (!ReadField32(&cursor, end, &size))
289 break;
290 current = NextChar(&cursor, end);
291 }
292
293 if (current == kDelimSemicolon) {
294 if (size > kMaxPRegFileSize)
295 break;
296 data.resize(size);
297 if (!ReadFieldBinary(&cursor, end, size, data.data()))
298 break;
299 current = NextChar(&cursor, end);
300 }
301
302 if (current != kDelimBracketClose)
303 break;
304
305 // Process the record if it is within the |root| subtree.
306 if (base::StartsWith(base::i18n::ToLower(key_name),
307 base::i18n::ToLower(root),
308 base::CompareCase::SENSITIVE))
309 HandleRecord(key_name.substr(root.size()), value, type, data, dict);
310 }
311
312 LOG(ERROR) << "Error parsing " << file_path.value() << " at offset "
313 << reinterpret_cast<const uint8_t*>(cursor - 1) -
314 mapped_file.data();
315 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
316 return false;
317 }
318
319 } // namespace preg_parser
320 } // namespace policy
OLDNEW
« no previous file with comments | « components/policy/core/common/preg_parser_win.h ('k') | components/policy/core/common/preg_parser_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698