1// Copyright 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/registry_dict_win.h"
6
7#include "base/json/json_reader.h"
8#include "base/stl_util.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/sys_byteorder.h"
13#include "base/values.h"
14#include "base/win/registry.h"
15#include "components/policy/core/common/schema.h"
16
17using base::win::RegistryKeyIterator;
18using base::win::RegistryValueIterator;
19
20namespace policy {
21
22namespace {
23
24// Converts a value (as read from the registry) to meet |schema|, converting
25// types as necessary. Unconvertible types will show up as NULL values in the
26// result.
27scoped_ptr<base::Value> ConvertValue(const base::Value& value,
28                                     const Schema& schema) {
29  if (!schema.valid())
30    return make_scoped_ptr(value.DeepCopy()).Pass();
31
32  // If the type is good already, go with it.
33  if (value.IsType(schema.type())) {
34    // Recurse for complex types.
35    const base::DictionaryValue* dict = NULL;
36    const base::ListValue* list = NULL;
37    if (value.GetAsDictionary(&dict)) {
38      scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
39      for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
40           entry.Advance()) {
41        scoped_ptr<base::Value> converted =
42            ConvertValue(entry.value(), schema.GetProperty(entry.key()));
43        if (converted)
44          result->SetWithoutPathExpansion(entry.key(), converted.release());
45      }
46      return result.PassAs<base::Value>();
47    } else if (value.GetAsList(&list)) {
48      scoped_ptr<base::ListValue> result(new base::ListValue());
49      for (base::ListValue::const_iterator entry(list->begin());
50           entry != list->end(); ++entry) {
51        scoped_ptr<base::Value> converted =
52            ConvertValue(**entry, schema.GetItems());
53        if (converted)
54          result->Append(converted.release());
55      }
56      return result.PassAs<base::Value>();
57    }
58    return make_scoped_ptr(value.DeepCopy()).Pass();
59  }
60
61  // Else, do some conversions to map windows registry data types to JSON types.
62  std::string string_value;
63  int int_value = 0;
64  switch (schema.type()) {
65    case base::Value::TYPE_NULL: {
66      return make_scoped_ptr(base::Value::CreateNullValue()).Pass();
67    }
68    case base::Value::TYPE_BOOLEAN: {
69      // Accept booleans encoded as either string or integer.
70      if (value.GetAsInteger(&int_value) ||
71          (value.GetAsString(&string_value) &&
72           base::StringToInt(string_value, &int_value))) {
73        return scoped_ptr<base::Value>(
74            new base::FundamentalValue(int_value != 0));
75      }
76      break;
77    }
78    case base::Value::TYPE_INTEGER: {
79      // Integers may be string-encoded.
80      if (value.GetAsString(&string_value) &&
81          base::StringToInt(string_value, &int_value)) {
82        return scoped_ptr<base::Value>(new base::FundamentalValue(int_value));
83      }
84      break;
85    }
86    case base::Value::TYPE_DOUBLE: {
87      // Doubles may be string-encoded or integer-encoded.
88      double double_value = 0;
89      if (value.GetAsDouble(&double_value) ||
90          (value.GetAsString(&string_value) &&
91           base::StringToDouble(string_value, &double_value))) {
92        return scoped_ptr<base::Value>(
93            new base::FundamentalValue(double_value));
94      }
95      break;
96    }
97    case base::Value::TYPE_LIST: {
98      // Lists are encoded as subkeys with numbered value in the registry.
99      const base::DictionaryValue* dict = NULL;
100      if (value.GetAsDictionary(&dict)) {
101        scoped_ptr<base::ListValue> result(new base::ListValue());
102        for (int i = 1; ; ++i) {
103          const base::Value* entry = NULL;
104          if (!dict->Get(base::IntToString(i), &entry))
105            break;
106          scoped_ptr<base::Value> converted =
107              ConvertValue(*entry, schema.GetItems());
108          if (converted)
109            result->Append(converted.release());
110        }
111        return result.PassAs<base::Value>();
112      }
113      // Fall through in order to accept lists encoded as JSON strings.
114    }
115    case base::Value::TYPE_DICTIONARY: {
116      // Dictionaries may be encoded as JSON strings.
117      if (value.GetAsString(&string_value)) {
118        scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
119        if (result && result->IsType(schema.type()))
120          return result.Pass();
121      }
122      break;
123    }
124    case base::Value::TYPE_STRING:
125    case base::Value::TYPE_BINARY:
126      // No conversion possible.
127      break;
128  }
129
130  LOG(WARNING) << "Failed to convert " << value.GetType()
131               << " to " << schema.type();
132  return scoped_ptr<base::Value>();
133}
134
135}  // namespace
136
137bool CaseInsensitiveStringCompare::operator()(const std::string& a,
138                                              const std::string& b) const {
139  return base::strcasecmp(a.c_str(), b.c_str()) < 0;
140}
141
142RegistryDict::RegistryDict() {}
143
144RegistryDict::~RegistryDict() {
145  ClearKeys();
146  ClearValues();
147}
148
149RegistryDict* RegistryDict::GetKey(const std::string& name) {
150  KeyMap::iterator entry = keys_.find(name);
151  return entry != keys_.end() ? entry->second : NULL;
152}
153
154const RegistryDict* RegistryDict::GetKey(const std::string& name) const {
155  KeyMap::const_iterator entry = keys_.find(name);
156  return entry != keys_.end() ? entry->second : NULL;
157}
158
159void RegistryDict::SetKey(const std::string& name,
160                          scoped_ptr<RegistryDict> dict) {
161  if (!dict) {
162    RemoveKey(name);
163    return;
164  }
165
166  RegistryDict*& entry = keys_[name];
167  delete entry;
168  entry = dict.release();
169}
170
171scoped_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) {
172  scoped_ptr<RegistryDict> result;
173  KeyMap::iterator entry = keys_.find(name);
174  if (entry != keys_.end()) {
175    result.reset(entry->second);
176    keys_.erase(entry);
177  }
178  return result.Pass();
179}
180
181void RegistryDict::ClearKeys() {
182  STLDeleteValues(&keys_);
183}
184
185base::Value* RegistryDict::GetValue(const std::string& name) {
186  ValueMap::iterator entry = values_.find(name);
187  return entry != values_.end() ? entry->second : NULL;
188}
189
190const base::Value* RegistryDict::GetValue(const std::string& name) const {
191  ValueMap::const_iterator entry = values_.find(name);
192  return entry != values_.end() ? entry->second : NULL;
193}
194
195void RegistryDict::SetValue(const std::string& name,
196                            scoped_ptr<base::Value> dict) {
197  if (!dict) {
198    RemoveValue(name);
199    return;
200  }
201
202  base::Value*& entry = values_[name];
203  delete entry;
204  entry = dict.release();
205}
206
207scoped_ptr<base::Value> RegistryDict::RemoveValue(const std::string& name) {
208  scoped_ptr<base::Value> result;
209  ValueMap::iterator entry = values_.find(name);
210  if (entry != values_.end()) {
211    result.reset(entry->second);
212    values_.erase(entry);
213  }
214  return result.Pass();
215}
216
217void RegistryDict::ClearValues() {
218  STLDeleteValues(&values_);
219}
220
221void RegistryDict::Merge(const RegistryDict& other) {
222  for (KeyMap::const_iterator entry(other.keys_.begin());
223       entry != other.keys_.end(); ++entry) {
224    RegistryDict*& subdict = keys_[entry->first];
225    if (!subdict)
226      subdict = new RegistryDict();
227    subdict->Merge(*entry->second);
228  }
229
230  for (ValueMap::const_iterator entry(other.values_.begin());
231       entry != other.values_.end(); ++entry) {
232    SetValue(entry->first, make_scoped_ptr(entry->second->DeepCopy()).Pass());
233  }
234}
235
236void RegistryDict::Swap(RegistryDict* other) {
237  keys_.swap(other->keys_);
238  values_.swap(other->values_);
239}
240
241void RegistryDict::ReadRegistry(HKEY hive, const base::string16& root) {
242  ClearKeys();
243  ClearValues();
244
245  // First, read all the values of the key.
246  for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
247    const std::string name = base::UTF16ToUTF8(it.Name());
248    switch (it.Type()) {
249      case REG_SZ:
250      case REG_EXPAND_SZ:
251        SetValue(name,
252                 scoped_ptr<base::Value>(
253                     new base::StringValue(base::UTF16ToUTF8(it.Value()))));
254        continue;
255      case REG_DWORD_LITTLE_ENDIAN:
256      case REG_DWORD_BIG_ENDIAN:
257        if (it.ValueSize() == sizeof(DWORD)) {
258          DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
259          if (it.Type() == REG_DWORD_BIG_ENDIAN)
260            dword_value = base::NetToHost32(dword_value);
261          else
262            dword_value = base::ByteSwapToLE32(dword_value);
263          SetValue(name,
264                   scoped_ptr<base::Value>(new base::FundamentalValue(
265                       static_cast<int>(dword_value))));
266          continue;
267        }
268      case REG_NONE:
269      case REG_LINK:
270      case REG_MULTI_SZ:
271      case REG_RESOURCE_LIST:
272      case REG_FULL_RESOURCE_DESCRIPTOR:
273      case REG_RESOURCE_REQUIREMENTS_LIST:
274      case REG_QWORD_LITTLE_ENDIAN:
275        // Unsupported type, message gets logged below.
276        break;
277    }
278
279    LOG(WARNING) << "Failed to read hive " << hive << " at "
280                 << root << "\\" << name
281                 << " type " << it.Type();
282  }
283
284  // Recurse for all subkeys.
285  for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
286    std::string name(base::UTF16ToUTF8(it.Name()));
287    scoped_ptr<RegistryDict> subdict(new RegistryDict());
288    subdict->ReadRegistry(hive, root + L"\\" + it.Name());
289    SetKey(name, subdict.Pass());
290  }
291}
292
293scoped_ptr<base::Value> RegistryDict::ConvertToJSON(
294    const Schema& schema) const {
295  base::Value::Type type =
296      schema.valid() ? schema.type() : base::Value::TYPE_DICTIONARY;
297  switch (type) {
298    case base::Value::TYPE_DICTIONARY: {
299      scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
300      for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
301           entry != values_.end(); ++entry) {
302        Schema subschema =
303            schema.valid() ? schema.GetProperty(entry->first) : Schema();
304        scoped_ptr<base::Value> converted =
305            ConvertValue(*entry->second, subschema);
306        if (converted)
307          result->SetWithoutPathExpansion(entry->first, converted.release());
308      }
309      for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
310           entry != keys_.end(); ++entry) {
311        Schema subschema =
312            schema.valid() ? schema.GetProperty(entry->first) : Schema();
313        scoped_ptr<base::Value> converted =
314            entry->second->ConvertToJSON(subschema);
315        if (converted)
316          result->SetWithoutPathExpansion(entry->first, converted.release());
317      }
318      return result.PassAs<base::Value>();
319    }
320    case base::Value::TYPE_LIST: {
321      scoped_ptr<base::ListValue> result(new base::ListValue());
322      Schema item_schema = schema.valid() ? schema.GetItems() : Schema();
323      for (int i = 1; ; ++i) {
324        const std::string name(base::IntToString(i));
325        const RegistryDict* key = GetKey(name);
326        if (key) {
327          scoped_ptr<base::Value> converted = key->ConvertToJSON(item_schema);
328          if (converted)
329            result->Append(converted.release());
330          continue;
331        }
332        const base::Value* value = GetValue(name);
333        if (value) {
334          scoped_ptr<base::Value> converted = ConvertValue(*value, item_schema);
335          if (converted)
336            result->Append(converted.release());
337          continue;
338        }
339        break;
340      }
341      return result.PassAs<base::Value>();
342    }
343    default:
344      LOG(WARNING) << "Can't convert registry key to schema type " << type;
345  }
346
347  return scoped_ptr<base::Value>();
348}
349
350}  // namespace policy
351