Home | History | Annotate | Download | only in policy
      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 <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 "chrome/browser/policy/policy_load_status.h"
     24 #include "chrome/browser/policy/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 char16 kDelimBracketOpen = L'[';
     37 const char16 kDelimBracketClose = L']';
     38 const char16 kDelimSemicolon = L';';
     39 
     40 // Registry path separator.
     41 const 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 char16 is available.
     56   if (*cursor + sizeof(char16) > end)
     57     return -1;
     58 
     59   int result = **cursor | (*(*cursor + 1) << 8);
     60   *cursor += sizeof(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                      int size,
     68                      uint8* data) {
     69   const uint8* field_end = *cursor + size;
     70   if (field_end > end)
     71     return false;
     72   std::copy(*cursor, field_end, data);
     73   *cursor = field_end;
     74   return true;
     75 }
     76 
     77 bool ReadField32(const uint8** cursor, const uint8* end, uint32* data) {
     78   uint32 value = 0;
     79   if (!ReadFieldBinary(cursor, end, sizeof(uint32),
     80                        reinterpret_cast<uint8*>(&value))) {
     81     return false;
     82   }
     83   *data = base::ByteSwapToLE32(value);
     84   return true;
     85 }
     86 
     87 // Reads a string field from a  file.
     88 bool ReadFieldString(const uint8** cursor, const uint8* end, string16* str) {
     89   int current = -1;
     90   while ((current = NextChar(cursor, end)) > 0x0000)
     91     *str += current;
     92 
     93   return current == L'\0';
     94 }
     95 
     96 std::string DecodePRegStringValue(const std::vector<uint8>& data) {
     97   size_t len = data.size() / sizeof(char16);
     98   if (len <= 0)
     99     return std::string();
    100 
    101   const char16* chars = reinterpret_cast<const char16*>(vector_as_array(&data));
    102   string16 result;
    103   std::transform(chars, chars + len - 1, std::back_inserter(result),
    104                  std::ptr_fun(base::ByteSwapToLE16));
    105   return UTF16ToUTF8(result);
    106 }
    107 
    108 // Decodes a value from a PReg file given as a uint8 vector.
    109 bool DecodePRegValue(uint32 type,
    110                      const std::vector<uint8>& data,
    111                      scoped_ptr<base::Value>* value) {
    112   switch (type) {
    113     case REG_SZ:
    114     case REG_EXPAND_SZ:
    115       value->reset(base::Value::CreateStringValue(DecodePRegStringValue(data)));
    116       return true;
    117     case REG_DWORD_LITTLE_ENDIAN:
    118     case REG_DWORD_BIG_ENDIAN:
    119       if (data.size() == sizeof(uint32)) {
    120         uint32 val = *reinterpret_cast<const uint32*>(vector_as_array(&data));
    121         if (type == REG_DWORD_BIG_ENDIAN)
    122           val = base::NetToHost32(val);
    123         else
    124           val = base::ByteSwapToLE32(val);
    125         value->reset(base::Value::CreateIntegerValue(static_cast<int>(val)));
    126         return true;
    127       } else {
    128         LOG(ERROR) << "Bad data size " << data.size();
    129       }
    130       break;
    131     case REG_NONE:
    132     case REG_LINK:
    133     case REG_MULTI_SZ:
    134     case REG_RESOURCE_LIST:
    135     case REG_FULL_RESOURCE_DESCRIPTOR:
    136     case REG_RESOURCE_REQUIREMENTS_LIST:
    137     case REG_QWORD_LITTLE_ENDIAN:
    138     default:
    139       LOG(ERROR) << "Unsupported registry data type " << type;
    140   }
    141 
    142   return false;
    143 }
    144 
    145 // Adds the record data passed via parameters to |dict| in case the data is
    146 // relevant policy for Chromium.
    147 void HandleRecord(const string16& key_name,
    148                   const string16& value,
    149                   uint32 type,
    150                   const std::vector<uint8>& data,
    151                   RegistryDict* dict) {
    152   // Locate/create the dictionary to place the value in.
    153   std::vector<string16> path;
    154 
    155   Tokenize(key_name, kRegistryPathSeparator, &path);
    156   for (std::vector<string16>::const_iterator entry(path.begin());
    157        entry != path.end(); ++entry) {
    158     if (entry->empty())
    159       continue;
    160     const std::string name = UTF16ToUTF8(*entry);
    161     RegistryDict* subdict = dict->GetKey(name);
    162     if (!subdict) {
    163       subdict = new RegistryDict();
    164       dict->SetKey(name, make_scoped_ptr(subdict));
    165     }
    166     dict = subdict;
    167   }
    168 
    169   if (value.empty())
    170     return;
    171 
    172   std::string value_name(UTF16ToUTF8(value));
    173   if (!StartsWithASCII(value_name, kActionTriggerPrefix, true)) {
    174     scoped_ptr<base::Value> value;
    175     if (DecodePRegValue(type, data, &value))
    176       dict->SetValue(value_name, value.Pass());
    177     return;
    178   }
    179 
    180   std::string action_trigger(StringToLowerASCII(value_name.substr(
    181       arraysize(kActionTriggerPrefix) - 1)));
    182   if (action_trigger == kActionTriggerDeleteValues) {
    183     std::vector<std::string> values;
    184     Tokenize(DecodePRegStringValue(data), ";", &values);
    185     for (std::vector<std::string>::const_iterator value(values.begin());
    186          value != values.end(); ++value) {
    187       dict->RemoveValue(*value);
    188     }
    189   } else if (StartsWithASCII(action_trigger, kActionTriggerDeleteKeys, true)) {
    190     std::vector<std::string> keys;
    191     Tokenize(DecodePRegStringValue(data), ";", &keys);
    192     for (std::vector<std::string>::const_iterator key(keys.begin());
    193          key != keys.end(); ++key) {
    194       dict->RemoveKey(*key);
    195     }
    196   } else if (StartsWithASCII(action_trigger, kActionTriggerDel, true)) {
    197     dict->RemoveValue(
    198         value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
    199                           arraysize(kActionTriggerDel) - 1));
    200   } else if (StartsWithASCII(action_trigger, kActionTriggerDelVals, true)) {
    201     // Delete all values.
    202     dict->ClearValues();
    203   } else if (StartsWithASCII(action_trigger, kActionTriggerSecureKey, true) ||
    204              StartsWithASCII(action_trigger, kActionTriggerSoft, true)) {
    205     // Doesn't affect values.
    206   } else {
    207     LOG(ERROR) << "Bad action trigger " << value_name;
    208   }
    209 }
    210 
    211 bool ReadFile(const base::FilePath& file_path,
    212               const string16& root,
    213               RegistryDict* dict,
    214               PolicyLoadStatusSample* status) {
    215   base::MemoryMappedFile mapped_file;
    216   if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
    217     PLOG(ERROR) << "Failed to map " << file_path.value();
    218     status->Add(POLICY_LOAD_STATUS_READ_ERROR);
    219     return false;
    220   }
    221 
    222   if (mapped_file.length() > kMaxPRegFileSize) {
    223     LOG(ERROR) << "PReg file " << file_path.value() << " too large: "
    224                << mapped_file.length();
    225     status->Add(POLICY_LOAD_STATUS_TOO_BIG);
    226     return false;
    227   }
    228 
    229   // Check the header.
    230   const int kHeaderSize = arraysize(kPRegFileHeader);
    231   if (mapped_file.length() < kHeaderSize ||
    232       memcmp(kPRegFileHeader, mapped_file.data(), kHeaderSize) != 0) {
    233     LOG(ERROR) << "Bad policy file " << file_path.value();
    234     status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
    235     return false;
    236   }
    237 
    238   // Parse file contents, which is UCS-2 and little-endian. The latter I
    239   // couldn't find documentation on, but the example I saw were all
    240   // little-endian. It'd be interesting to check on big-endian hardware.
    241   const uint8* cursor = mapped_file.data() + kHeaderSize;
    242   const uint8* end = mapped_file.data() + mapped_file.length();
    243   while (true) {
    244     if (cursor == end)
    245       return true;
    246 
    247     if (NextChar(&cursor, end) != kDelimBracketOpen)
    248       break;
    249 
    250     // Read the record fields.
    251     string16 key_name;
    252     string16 value;
    253     uint32 type = 0;
    254     uint32 size = 0;
    255     std::vector<uint8> data;
    256 
    257     if (!ReadFieldString(&cursor, end, &key_name))
    258       break;
    259 
    260     int current = NextChar(&cursor, end);
    261     if (current == kDelimSemicolon) {
    262       if (!ReadFieldString(&cursor, end, &value))
    263         break;
    264       current = NextChar(&cursor, end);
    265     }
    266 
    267     if (current == kDelimSemicolon) {
    268       if (!ReadField32(&cursor, end, &type))
    269         break;
    270       current = NextChar(&cursor, end);
    271     }
    272 
    273     if (current == kDelimSemicolon) {
    274       if (!ReadField32(&cursor, end, &size))
    275         break;
    276       current = NextChar(&cursor, end);
    277     }
    278 
    279     if (current == kDelimSemicolon) {
    280       if (size > kMaxPRegFileSize)
    281         break;
    282       data.resize(size);
    283       if (!ReadFieldBinary(&cursor, end, size, vector_as_array(&data)))
    284         break;
    285       current = NextChar(&cursor, end);
    286     }
    287 
    288     if (current != kDelimBracketClose)
    289       break;
    290 
    291     // Process the record if it is within the |root| subtree.
    292     if (StartsWith(key_name, root, false))
    293       HandleRecord(key_name.substr(root.size()), value, type, data, dict);
    294   }
    295 
    296   LOG(ERROR) << "Error parsing " << file_path.value() << " at offset "
    297              << reinterpret_cast<const uint8*>(cursor - 1) - mapped_file.data();
    298   status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
    299   return false;
    300 }
    301 
    302 }  // namespace preg_parser
    303 }  // namespace policy
    304