Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / prefs / pref_hash_store_impl.cc
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 "chrome/browser/prefs/pref_hash_store_impl.h"
6
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/values.h"
10 #include "chrome/browser/prefs/pref_hash_store_transaction.h"
11 #include "chrome/browser/prefs/tracked/hash_store_contents.h"
12
13 namespace {
14
15 // Returns true if the dictionary of hashes stored in |contents| is trusted
16 // (which implies unknown values can be trusted as newly tracked values).
17 bool IsHashDictionaryTrusted(const PrefHashCalculator& calculator,
18                              const HashStoreContents& contents) {
19   const base::DictionaryValue* store_contents = contents.GetContents();
20   std::string super_mac = contents.GetSuperMac();
21   // The store must be initialized and have a valid super MAC to be trusted.
22   return store_contents && !super_mac.empty() &&
23          calculator.Validate(contents.hash_store_id(),
24                              store_contents,
25                              super_mac) == PrefHashCalculator::VALID;
26 }
27
28 }  // namespace
29
30 class PrefHashStoreImpl::PrefHashStoreTransactionImpl
31     : public PrefHashStoreTransaction {
32  public:
33   // Constructs a PrefHashStoreTransactionImpl which can use the private
34   // members of its |outer| PrefHashStoreImpl.
35   explicit PrefHashStoreTransactionImpl(PrefHashStoreImpl* outer);
36   virtual ~PrefHashStoreTransactionImpl();
37
38   // PrefHashStoreTransaction implementation.
39   virtual ValueState CheckValue(const std::string& path,
40                                 const base::Value* value) const OVERRIDE;
41   virtual void StoreHash(const std::string& path,
42                          const base::Value* value) OVERRIDE;
43   virtual ValueState CheckSplitValue(
44       const std::string& path,
45       const base::DictionaryValue* initial_split_value,
46       std::vector<std::string>* invalid_keys) const OVERRIDE;
47   virtual void StoreSplitHash(
48       const std::string& path,
49       const base::DictionaryValue* split_value) OVERRIDE;
50
51  private:
52   bool GetSplitMacs(const std::string& path,
53                     std::map<std::string, std::string>* split_macs) const;
54   PrefHashStoreImpl* outer_;
55   bool has_changed_;
56
57   DISALLOW_COPY_AND_ASSIGN(PrefHashStoreTransactionImpl);
58 };
59
60 PrefHashStoreImpl::PrefHashStoreImpl(const std::string& seed,
61                                      const std::string& device_id,
62                                      scoped_ptr<HashStoreContents> contents)
63     : pref_hash_calculator_(seed, device_id),
64       contents_(contents.Pass()),
65       initial_hashes_dictionary_trusted_(
66           IsHashDictionaryTrusted(pref_hash_calculator_, *contents_)),
67       has_pending_write_(false) {
68   DCHECK(contents_);
69   UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted",
70                         initial_hashes_dictionary_trusted_);
71 }
72
73 PrefHashStoreImpl::~PrefHashStoreImpl() {}
74
75 void PrefHashStoreImpl::Reset() {
76   contents_->Reset();
77 }
78
79 scoped_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction() {
80   return scoped_ptr<PrefHashStoreTransaction>(
81       new PrefHashStoreTransactionImpl(this));
82 }
83
84 PrefHashStoreImpl::StoreVersion PrefHashStoreImpl::GetCurrentVersion() const {
85   if (!contents_->IsInitialized())
86     return VERSION_UNINITIALIZED;
87
88   int current_version;
89   if (!contents_->GetVersion(&current_version)) {
90     return VERSION_PRE_MIGRATION;
91   }
92
93   DCHECK_GT(current_version, VERSION_PRE_MIGRATION);
94   return static_cast<StoreVersion>(current_version);
95 }
96
97 void PrefHashStoreImpl::CommitPendingWrite() {
98   if (has_pending_write_) {
99     contents_->CommitPendingWrite();
100     has_pending_write_ = false;
101   }
102 }
103
104 PrefHashStoreImpl::PrefHashStoreTransactionImpl::PrefHashStoreTransactionImpl(
105     PrefHashStoreImpl* outer) : outer_(outer), has_changed_(false) {
106 }
107
108 PrefHashStoreImpl::PrefHashStoreTransactionImpl::
109     ~PrefHashStoreTransactionImpl() {
110   // Update the super MAC if and only if the hashes dictionary has been
111   // modified in this transaction.
112   if (has_changed_) {
113     // Get the dictionary of hashes (or NULL if it doesn't exist).
114     const base::DictionaryValue* hashes_dict = outer_->contents_->GetContents();
115     outer_->contents_->SetSuperMac(outer_->pref_hash_calculator_.Calculate(
116         outer_->contents_->hash_store_id(), hashes_dict));
117
118     outer_->has_pending_write_ = true;
119   }
120
121   // Mark this hash store has having been updated to the latest version (in
122   // practice only initialization transactions will actually do this, but
123   // since they always occur before minor update transaction it's okay
124   // to unconditionally do this here). Only do this if this store's version
125   // isn't already at VERSION_LATEST (to avoid scheduling a write when
126   // unecessary). Note, this is outside of |if (has_changed)| to also seed
127   // version number of otherwise unchanged profiles.
128   int current_version;
129   if (!outer_->contents_->GetVersion(&current_version) ||
130       current_version != VERSION_LATEST) {
131     outer_->contents_->SetVersion(VERSION_LATEST);
132     outer_->has_pending_write_ = true;
133   }
134 }
135
136 PrefHashStoreTransaction::ValueState
137 PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckValue(
138     const std::string& path, const base::Value* initial_value) const {
139   const base::DictionaryValue* hashed_prefs = outer_->contents_->GetContents();
140
141   std::string last_hash;
142   if (hashed_prefs)
143     hashed_prefs->GetString(path, &last_hash);
144
145   if (last_hash.empty()) {
146     // In the absence of a hash for this pref, always trust a NULL value, but
147     // only trust an existing value if the initial hashes dictionary is trusted.
148     return (!initial_value || outer_->initial_hashes_dictionary_trusted_) ?
149                TRUSTED_UNKNOWN_VALUE : UNTRUSTED_UNKNOWN_VALUE;
150   }
151
152   PrefHashCalculator::ValidationResult validation_result =
153       outer_->pref_hash_calculator_.Validate(path, initial_value, last_hash);
154   switch (validation_result) {
155     case PrefHashCalculator::VALID:
156       return UNCHANGED;
157     case PrefHashCalculator::VALID_WEAK_LEGACY:
158       return WEAK_LEGACY;
159     case PrefHashCalculator::VALID_SECURE_LEGACY:
160       return SECURE_LEGACY;
161     case PrefHashCalculator::INVALID:
162       return initial_value ? CHANGED : CLEARED;
163   }
164   NOTREACHED() << "Unexpected PrefHashCalculator::ValidationResult: "
165                << validation_result;
166   return UNTRUSTED_UNKNOWN_VALUE;
167 }
168
169 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreHash(
170     const std::string& path, const base::Value* new_value) {
171   const std::string mac =
172       outer_->pref_hash_calculator_.Calculate(path, new_value);
173   (*outer_->contents_->GetMutableContents())->SetString(path, mac);
174   has_changed_ = true;
175 }
176
177 PrefHashStoreTransaction::ValueState
178 PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckSplitValue(
179     const std::string& path,
180     const base::DictionaryValue* initial_split_value,
181     std::vector<std::string>* invalid_keys) const {
182   DCHECK(invalid_keys && invalid_keys->empty());
183
184   std::map<std::string, std::string> split_macs;
185   const bool has_hashes = GetSplitMacs(path, &split_macs);
186
187   // Treat NULL and empty the same; otherwise we would need to store a hash
188   // for the entire dictionary (or some other special beacon) to
189   // differentiate these two cases which are really the same for
190   // dictionaries.
191   if (!initial_split_value || initial_split_value->empty())
192     return has_hashes ? CLEARED : UNCHANGED;
193
194   if (!has_hashes) {
195     return outer_->initial_hashes_dictionary_trusted_ ?
196         TRUSTED_UNKNOWN_VALUE : UNTRUSTED_UNKNOWN_VALUE;
197   }
198
199   bool has_secure_legacy_id_hashes = false;
200   std::string keyed_path(path);
201   keyed_path.push_back('.');
202   const size_t common_part_length = keyed_path.length();
203   for (base::DictionaryValue::Iterator it(*initial_split_value); !it.IsAtEnd();
204        it.Advance()) {
205     std::map<std::string, std::string>::iterator entry =
206         split_macs.find(it.key());
207     if (entry == split_macs.end()) {
208       invalid_keys->push_back(it.key());
209     } else {
210       // Keep the common part from the old |keyed_path| and replace the key to
211       // get the new |keyed_path|.
212       keyed_path.replace(common_part_length, std::string::npos, it.key());
213       switch (outer_->pref_hash_calculator_.Validate(
214           keyed_path, &it.value(), entry->second)) {
215         case PrefHashCalculator::VALID:
216           break;
217         case WEAK_LEGACY:
218           // Split tracked preferences were introduced after the migration from
219           // the weaker legacy algorithm started so no migration is expected,
220           // but declare it invalid in Release builds anyways.
221           NOTREACHED();
222           invalid_keys->push_back(it.key());
223           break;
224         case SECURE_LEGACY:
225           // Secure legacy device IDs based hashes are still accepted, but we
226           // should make sure to notify the caller for him to update the legacy
227           // hashes.
228           has_secure_legacy_id_hashes = true;
229           break;
230         case PrefHashCalculator::INVALID:
231           invalid_keys->push_back(it.key());
232           break;
233       }
234       // Remove processed MACs, remaining MACs at the end will also be
235       // considered invalid.
236       split_macs.erase(entry);
237     }
238   }
239
240   // Anything left in the map is missing from the data.
241   for (std::map<std::string, std::string>::const_iterator it =
242            split_macs.begin();
243        it != split_macs.end();
244        ++it) {
245     invalid_keys->push_back(it->first);
246   }
247
248   return invalid_keys->empty()
249       ? (has_secure_legacy_id_hashes ? SECURE_LEGACY : UNCHANGED)
250       : CHANGED;
251 }
252
253 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreSplitHash(
254     const std::string& path,
255     const base::DictionaryValue* split_value) {
256   scoped_ptr<HashStoreContents::MutableDictionary> mutable_dictionary =
257       outer_->contents_->GetMutableContents();
258   (*mutable_dictionary)->Remove(path, NULL);
259
260   if (split_value) {
261     std::string keyed_path(path);
262     keyed_path.push_back('.');
263     const size_t common_part_length = keyed_path.length();
264     for (base::DictionaryValue::Iterator it(*split_value); !it.IsAtEnd();
265          it.Advance()) {
266       // Keep the common part from the old |keyed_path| and replace the key to
267       // get the new |keyed_path|.
268       keyed_path.replace(common_part_length, std::string::npos, it.key());
269       (*mutable_dictionary)->SetString(
270           keyed_path,
271           outer_->pref_hash_calculator_.Calculate(keyed_path, &it.value()));
272     }
273   }
274   has_changed_ = true;
275 }
276
277 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::GetSplitMacs(
278     const std::string& key,
279     std::map<std::string, std::string>* split_macs) const {
280   DCHECK(split_macs);
281   DCHECK(split_macs->empty());
282
283   const base::DictionaryValue* hashed_prefs = outer_->contents_->GetContents();
284   const base::DictionaryValue* split_mac_dictionary = NULL;
285   if (!hashed_prefs || !hashed_prefs->GetDictionary(key, &split_mac_dictionary))
286     return false;
287   for (base::DictionaryValue::Iterator it(*split_mac_dictionary); !it.IsAtEnd();
288        it.Advance()) {
289     std::string mac_string;
290     if (!it.value().GetAsString(&mac_string)) {
291       NOTREACHED();
292       continue;
293     }
294     split_macs->insert(make_pair(it.key(), mac_string));
295   }
296   return true;
297 }