Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/policy/core/common/preg_parser_win.h" | 5 #include "components/policy/core/common/preg_parser.h" |
| 6 | 6 |
| 7 #include <windows.h> | |
| 8 #include <stddef.h> | 7 #include <stddef.h> |
| 9 #include <stdint.h> | 8 #include <stdint.h> |
| 10 | 9 |
| 11 #include <algorithm> | 10 #include <algorithm> |
| 12 #include <functional> | 11 #include <functional> |
| 13 #include <iterator> | 12 #include <iterator> |
| 13 #include <memory> | |
| 14 #include <string> | |
| 14 #include <utility> | 15 #include <utility> |
| 15 #include <vector> | 16 #include <vector> |
| 16 | 17 |
| 17 #include "base/files/file_path.h" | 18 #include "base/files/file_path.h" |
| 18 #include "base/files/memory_mapped_file.h" | 19 #include "base/files/memory_mapped_file.h" |
| 19 #include "base/i18n/case_conversion.h" | |
| 20 #include "base/logging.h" | 20 #include "base/logging.h" |
| 21 #include "base/macros.h" | 21 #include "base/macros.h" |
| 22 #include "base/memory/ptr_util.h" | 22 #include "base/memory/ptr_util.h" |
| 23 #include "base/strings/string16.h" | 23 #include "base/strings/string16.h" |
| 24 #include "base/strings/string_split.h" | 24 #include "base/strings/string_split.h" |
| 25 #include "base/strings/string_util.h" | 25 #include "base/strings/string_util.h" |
| 26 #include "base/strings/utf_string_conversions.h" | 26 #include "base/strings/utf_string_conversions.h" |
| 27 #include "base/sys_byteorder.h" | 27 #include "base/sys_byteorder.h" |
| 28 #include "base/values.h" | 28 #include "base/values.h" |
| 29 #include "components/policy/core/common/policy_load_status.h" | 29 #include "components/policy/core/common/policy_load_status.h" |
| 30 #include "components/policy/core/common/registry_dict_win.h" | 30 #include "components/policy/core/common/registry_dict.h" |
| 31 | 31 |
| 32 namespace policy { | 32 #if defined(OS_WIN) |
| 33 namespace preg_parser { | 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 | |
| 34 | 49 |
| 35 const char kPRegFileHeader[8] = | 50 using RegistryDict = policy::RegistryDict; |
| 36 { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' }; | 51 |
| 52 namespace { | |
| 37 | 53 |
| 38 // Maximum PReg file size we're willing to accept. | 54 // Maximum PReg file size we're willing to accept. |
| 39 const int64_t kMaxPRegFileSize = 1024 * 1024 * 16; | 55 const int64_t kMaxPRegFileSize = 1024 * 1024 * 16; |
| 40 | 56 |
| 41 // Constants for PReg file delimiters. | 57 // Constants for PReg file delimiters. |
| 42 const base::char16 kDelimBracketOpen = L'['; | 58 const base::char16 kDelimBracketOpen = L'['; |
| 43 const base::char16 kDelimBracketClose = L']'; | 59 const base::char16 kDelimBracketClose = L']'; |
| 44 const base::char16 kDelimSemicolon = L';'; | 60 const base::char16 kDelimSemicolon = L';'; |
| 45 | 61 |
| 46 // Registry path separator. | 62 // Registry path separator. |
| 47 const base::char16 kRegistryPathSeparator[] = L"\\"; | 63 const base::string16 kRegistryPathSeparator = base::ASCIIToUTF16("\\"); |
| 48 | 64 |
| 49 // Magic strings for the PReg value field to trigger special actions. | 65 // Magic strings for the PReg value field to trigger special actions. |
| 50 const char kActionTriggerPrefix[] = "**"; | 66 const char kActionTriggerPrefix[] = "**"; |
| 51 const char kActionTriggerDeleteValues[] = "deletevalues"; | 67 const char kActionTriggerDeleteValues[] = "deletevalues"; |
| 52 const char kActionTriggerDel[] = "del."; | 68 const char kActionTriggerDel[] = "del."; |
| 53 const char kActionTriggerDelVals[] = "delvals"; | 69 const char kActionTriggerDelVals[] = "delvals"; |
| 54 const char kActionTriggerDeleteKeys[] = "deletekeys"; | 70 const char kActionTriggerDeleteKeys[] = "deletekeys"; |
| 55 const char kActionTriggerSecureKey[] = "securekey"; | 71 const char kActionTriggerSecureKey[] = "securekey"; |
| 56 const char kActionTriggerSoft[] = "soft"; | 72 const char kActionTriggerSoft[] = "soft"; |
| 57 | 73 |
| 58 // Returns the character at |cursor| and increments it, unless the end is here | 74 // Returns the character at |cursor| and increments it, unless the end is here |
| 59 // in which case -1 is returned. | 75 // in which case -1 is returned. |
| 60 int NextChar(const uint8_t** cursor, const uint8_t* end) { | 76 int NextChar(const uint8_t** cursor, const uint8_t* end) { |
| 61 // Only read the character if a full base::char16 is available. | 77 // Only read the character if a full base::char16 is available. |
| 62 if (*cursor + sizeof(base::char16) > end) | 78 // This comparison makes sure no overflow can happen. |
| 79 if (*cursor >= end || | |
| 80 static_cast<size_t>(end - *cursor) < sizeof(base::char16)) | |
| 63 return -1; | 81 return -1; |
| 64 | 82 |
| 65 int result = **cursor | (*(*cursor + 1) << 8); | 83 int result = **cursor | (*(*cursor + 1) << 8); |
| 66 *cursor += sizeof(base::char16); | 84 *cursor += sizeof(base::char16); |
| 67 return result; | 85 return result; |
| 68 } | 86 } |
| 69 | 87 |
| 70 // Reads a fixed-size field from a PReg file. | 88 // Reads a fixed-size field from a PReg file. |
| 71 bool ReadFieldBinary(const uint8_t** cursor, | 89 bool ReadFieldBinary(const uint8_t** cursor, |
| 72 const uint8_t* end, | 90 const uint8_t* end, |
| 73 uint32_t size, | 91 uint32_t size, |
| 74 uint8_t* data) { | 92 uint8_t* data) { |
| 75 if (size == 0) | 93 if (size == 0) |
| 76 return true; | 94 return true; |
| 77 | 95 |
| 96 // Be careful to prevent possible overflows here (don't do *cursor + size). | |
| 97 if (*cursor >= end || static_cast<size_t>(end - *cursor) < size) | |
| 98 return false; | |
| 78 const uint8_t* field_end = *cursor + size; | 99 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); | 100 std::copy(*cursor, field_end, data); |
| 82 *cursor = field_end; | 101 *cursor = field_end; |
| 83 return true; | 102 return true; |
| 84 } | 103 } |
| 85 | 104 |
| 86 bool ReadField32(const uint8_t** cursor, const uint8_t* end, uint32_t* data) { | 105 bool ReadField32(const uint8_t** cursor, const uint8_t* end, uint32_t* data) { |
| 87 uint32_t value = 0; | 106 uint32_t value = 0; |
| 88 if (!ReadFieldBinary(cursor, end, sizeof(uint32_t), | 107 if (!ReadFieldBinary(cursor, end, sizeof(uint32_t), |
| 89 reinterpret_cast<uint8_t*>(&value))) { | 108 reinterpret_cast<uint8_t*>(&value))) { |
| 90 return false; | 109 return false; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 147 case REG_FULL_RESOURCE_DESCRIPTOR: | 166 case REG_FULL_RESOURCE_DESCRIPTOR: |
| 148 case REG_RESOURCE_REQUIREMENTS_LIST: | 167 case REG_RESOURCE_REQUIREMENTS_LIST: |
| 149 case REG_QWORD_LITTLE_ENDIAN: | 168 case REG_QWORD_LITTLE_ENDIAN: |
| 150 default: | 169 default: |
| 151 LOG(ERROR) << "Unsupported registry data type " << type; | 170 LOG(ERROR) << "Unsupported registry data type " << type; |
| 152 } | 171 } |
| 153 | 172 |
| 154 return false; | 173 return false; |
| 155 } | 174 } |
| 156 | 175 |
| 157 // Adds the record data passed via parameters to |dict| in case the data is | 176 // Adds |value| and |data| to |dict| or an appropriate sub-dictionary indicated |
| 158 // relevant policy for Chromium. | 177 // by |key_name|. Creates sub-dictionaries if necessary. Also handles special |
| 178 // action triggers, see |kActionTrigger*|, that can, for instance, remove an | |
| 179 // existing value. | |
| 159 void HandleRecord(const base::string16& key_name, | 180 void HandleRecord(const base::string16& key_name, |
| 160 const base::string16& value, | 181 const base::string16& value, |
| 161 uint32_t type, | 182 uint32_t type, |
| 162 const std::vector<uint8_t>& data, | 183 const std::vector<uint8_t>& data, |
| 163 RegistryDict* dict) { | 184 RegistryDict* dict) { |
| 164 // Locate/create the dictionary to place the value in. | 185 // Locate/create the dictionary to place the value in. |
| 165 std::vector<base::string16> path; | 186 std::vector<base::string16> path; |
| 166 | 187 |
| 167 for (const base::string16& entry : | 188 for (const base::string16& entry : |
| 168 base::SplitString(key_name, kRegistryPathSeparator, | 189 base::SplitString(key_name, kRegistryPathSeparator, |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 185 if (!base::StartsWith(value_name, kActionTriggerPrefix, | 206 if (!base::StartsWith(value_name, kActionTriggerPrefix, |
| 186 base::CompareCase::SENSITIVE)) { | 207 base::CompareCase::SENSITIVE)) { |
| 187 std::unique_ptr<base::Value> value; | 208 std::unique_ptr<base::Value> value; |
| 188 if (DecodePRegValue(type, data, &value)) | 209 if (DecodePRegValue(type, data, &value)) |
| 189 dict->SetValue(value_name, std::move(value)); | 210 dict->SetValue(value_name, std::move(value)); |
| 190 return; | 211 return; |
| 191 } | 212 } |
| 192 | 213 |
| 193 std::string action_trigger(base::ToLowerASCII(value_name.substr( | 214 std::string action_trigger(base::ToLowerASCII(value_name.substr( |
| 194 arraysize(kActionTriggerPrefix) - 1))); | 215 arraysize(kActionTriggerPrefix) - 1))); |
| 195 if (action_trigger == kActionTriggerDeleteValues) { | 216 if (action_trigger == kActionTriggerDeleteValues) { |
|
pastarmovj
2016/11/09 12:26:06
please revert.
ljusten (tachyonic)
2016/11/09 15:09:56
Done.
| |
| 196 for (const std::string& value : | 217 for (const std::string& value : |
| 197 base::SplitString(DecodePRegStringValue(data), ";", | 218 base::SplitString(DecodePRegStringValue(data), ";", |
| 198 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) | 219 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) |
| 199 dict->RemoveValue(value); | 220 dict->RemoveValue(value); |
| 200 } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys, | 221 } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys, |
| 201 base::CompareCase::SENSITIVE)) { | 222 base::CompareCase::SENSITIVE)) { |
| 202 for (const std::string& key : | 223 for (const std::string& key : |
| 203 base::SplitString(DecodePRegStringValue(data), ";", | 224 base::SplitString(DecodePRegStringValue(data), ";", |
| 204 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) | 225 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) |
| 205 dict->RemoveKey(key); | 226 dict->RemoveKey(key); |
| 206 } else if (base::StartsWith(action_trigger, kActionTriggerDel, | 227 } else if (base::StartsWith(action_trigger, kActionTriggerDel, |
| 207 base::CompareCase::SENSITIVE)) { | 228 base::CompareCase::SENSITIVE)) { |
| 208 dict->RemoveValue( | 229 dict->RemoveValue( |
|
pastarmovj
2016/11/09 12:26:06
ditto.
ljusten (tachyonic)
2016/11/09 15:09:56
Done.
| |
| 209 value_name.substr(arraysize(kActionTriggerPrefix) - 1 + | 230 value_name.substr(arraysize(kActionTriggerPrefix) - 1 + |
| 210 arraysize(kActionTriggerDel) - 1)); | 231 arraysize(kActionTriggerDel) - 1)); |
| 211 } else if (base::StartsWith(action_trigger, kActionTriggerDelVals, | 232 } else if (base::StartsWith(action_trigger, kActionTriggerDelVals, |
| 212 base::CompareCase::SENSITIVE)) { | 233 base::CompareCase::SENSITIVE)) { |
| 213 // Delete all values. | 234 // Delete all values. |
| 214 dict->ClearValues(); | 235 dict->ClearValues(); |
| 215 } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey, | 236 } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey, |
| 216 base::CompareCase::SENSITIVE) || | 237 base::CompareCase::SENSITIVE) || |
| 217 base::StartsWith(action_trigger, kActionTriggerSoft, | 238 base::StartsWith(action_trigger, kActionTriggerSoft, |
| 218 base::CompareCase::SENSITIVE)) { | 239 base::CompareCase::SENSITIVE)) { |
| 219 // Doesn't affect values. | 240 // Doesn't affect values. |
| 220 } else { | 241 } else { |
| 221 LOG(ERROR) << "Bad action trigger " << value_name; | 242 LOG(ERROR) << "Bad action trigger " << value_name; |
| 222 } | 243 } |
| 223 } | 244 } |
| 224 | 245 |
| 246 } // namespace | |
| 247 | |
| 248 namespace policy { | |
| 249 namespace preg_parser { | |
| 250 | |
| 251 const char kPRegFileHeader[8] = | |
| 252 { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' }; | |
| 253 | |
| 225 bool ReadFile(const base::FilePath& file_path, | 254 bool ReadFile(const base::FilePath& file_path, |
| 226 const base::string16& root, | 255 const base::string16& root, |
| 227 RegistryDict* dict, | 256 RegistryDict* dict, |
| 228 PolicyLoadStatusSample* status) { | 257 PolicyLoadStatusSample* status) { |
| 229 base::MemoryMappedFile mapped_file; | 258 base::MemoryMappedFile mapped_file; |
| 230 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) { | 259 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) { |
| 231 PLOG(ERROR) << "Failed to map " << file_path.value(); | 260 PLOG(ERROR) << "Failed to map " << file_path.value(); |
| 232 status->Add(POLICY_LOAD_STATUS_READ_ERROR); | 261 status->Add(POLICY_LOAD_STATUS_READ_ERROR); |
| 233 return false; | 262 return false; |
| 234 } | 263 } |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 296 data.resize(size); | 325 data.resize(size); |
| 297 if (!ReadFieldBinary(&cursor, end, size, data.data())) | 326 if (!ReadFieldBinary(&cursor, end, size, data.data())) |
| 298 break; | 327 break; |
| 299 current = NextChar(&cursor, end); | 328 current = NextChar(&cursor, end); |
| 300 } | 329 } |
| 301 | 330 |
| 302 if (current != kDelimBracketClose) | 331 if (current != kDelimBracketClose) |
| 303 break; | 332 break; |
| 304 | 333 |
| 305 // Process the record if it is within the |root| subtree. | 334 // Process the record if it is within the |root| subtree. |
| 306 if (base::StartsWith(base::i18n::ToLower(key_name), | 335 if (base::StartsWith(key_name, root, base::CompareCase::INSENSITIVE_ASCII)) |
|
pastarmovj
2016/11/09 12:26:06
I just wonder if this can be used as a way to tric
ljusten (tachyonic)
2016/11/09 15:09:56
This is not possible. Here's the proof:
Theorem
-
| |
| 307 base::i18n::ToLower(root), | |
| 308 base::CompareCase::SENSITIVE)) | |
| 309 HandleRecord(key_name.substr(root.size()), value, type, data, dict); | 336 HandleRecord(key_name.substr(root.size()), value, type, data, dict); |
| 310 } | 337 } |
| 311 | 338 |
| 312 LOG(ERROR) << "Error parsing " << file_path.value() << " at offset " | 339 LOG(ERROR) << "Error parsing " << file_path.value() << " at offset " |
| 313 << reinterpret_cast<const uint8_t*>(cursor - 1) - | 340 << reinterpret_cast<const uint8_t*>(cursor - 1) - |
| 314 mapped_file.data(); | 341 mapped_file.data(); |
| 315 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR); | 342 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR); |
| 316 return false; | 343 return false; |
| 317 } | 344 } |
| 318 | 345 |
| 319 } // namespace preg_parser | 346 } // namespace preg_parser |
| 320 } // namespace policy | 347 } // namespace policy |
| OLD | NEW |