• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 class PrefHashStoreImpl::PrefHashStoreTransactionImpl
14     : public PrefHashStoreTransaction {
15  public:
16   // Constructs a PrefHashStoreTransactionImpl which can use the private
17   // members of its |outer| PrefHashStoreImpl.
18   PrefHashStoreTransactionImpl(PrefHashStoreImpl* outer,
19                                scoped_ptr<HashStoreContents> storage);
20   virtual ~PrefHashStoreTransactionImpl();
21 
22   // PrefHashStoreTransaction implementation.
23   virtual ValueState CheckValue(const std::string& path,
24                                 const base::Value* value) const OVERRIDE;
25   virtual void StoreHash(const std::string& path,
26                          const base::Value* value) OVERRIDE;
27   virtual ValueState CheckSplitValue(
28       const std::string& path,
29       const base::DictionaryValue* initial_split_value,
30       std::vector<std::string>* invalid_keys) const OVERRIDE;
31   virtual void StoreSplitHash(
32       const std::string& path,
33       const base::DictionaryValue* split_value) OVERRIDE;
34   virtual bool HasHash(const std::string& path) const OVERRIDE;
35   virtual void ImportHash(const std::string& path,
36                           const base::Value* hash) OVERRIDE;
37   virtual void ClearHash(const std::string& path) OVERRIDE;
38   virtual bool IsSuperMACValid() const OVERRIDE;
39   virtual bool StampSuperMac() OVERRIDE;
40 
41  private:
42   bool GetSplitMacs(const std::string& path,
43                     std::map<std::string, std::string>* split_macs) const;
44 
contents()45   HashStoreContents* contents() {
46     return outer_->legacy_hash_store_contents_
47                ? outer_->legacy_hash_store_contents_.get()
48                : contents_.get();
49   }
50 
contents() const51   const HashStoreContents* contents() const {
52     return outer_->legacy_hash_store_contents_
53                ? outer_->legacy_hash_store_contents_.get()
54                : contents_.get();
55   }
56 
57   PrefHashStoreImpl* outer_;
58   scoped_ptr<HashStoreContents> contents_;
59 
60   bool super_mac_valid_;
61   bool super_mac_dirty_;
62 
63   DISALLOW_COPY_AND_ASSIGN(PrefHashStoreTransactionImpl);
64 };
65 
PrefHashStoreImpl(const std::string & seed,const std::string & device_id,bool use_super_mac)66 PrefHashStoreImpl::PrefHashStoreImpl(const std::string& seed,
67                                      const std::string& device_id,
68                                      bool use_super_mac)
69     : pref_hash_calculator_(seed, device_id),
70       use_super_mac_(use_super_mac) {
71 }
72 
~PrefHashStoreImpl()73 PrefHashStoreImpl::~PrefHashStoreImpl() {
74 }
75 
set_legacy_hash_store_contents(scoped_ptr<HashStoreContents> legacy_hash_store_contents)76 void PrefHashStoreImpl::set_legacy_hash_store_contents(
77     scoped_ptr<HashStoreContents> legacy_hash_store_contents) {
78   legacy_hash_store_contents_ = legacy_hash_store_contents.Pass();
79 }
80 
BeginTransaction(scoped_ptr<HashStoreContents> storage)81 scoped_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction(
82     scoped_ptr<HashStoreContents> storage) {
83   return scoped_ptr<PrefHashStoreTransaction>(
84       new PrefHashStoreTransactionImpl(this, storage.Pass()));
85 }
86 
PrefHashStoreTransactionImpl(PrefHashStoreImpl * outer,scoped_ptr<HashStoreContents> storage)87 PrefHashStoreImpl::PrefHashStoreTransactionImpl::PrefHashStoreTransactionImpl(
88     PrefHashStoreImpl* outer,
89     scoped_ptr<HashStoreContents> storage)
90     : outer_(outer),
91       contents_(storage.Pass()),
92       super_mac_valid_(false),
93       super_mac_dirty_(false) {
94   if (!outer_->use_super_mac_)
95     return;
96 
97   // The store must be initialized and have a valid super MAC to be trusted.
98 
99   const base::DictionaryValue* store_contents = contents()->GetContents();
100   if (!store_contents)
101     return;
102 
103   std::string super_mac = contents()->GetSuperMac();
104   if (super_mac.empty())
105     return;
106 
107   super_mac_valid_ =
108       outer_->pref_hash_calculator_.Validate(
109           contents()->hash_store_id(), store_contents, super_mac) ==
110       PrefHashCalculator::VALID;
111 }
112 
113 PrefHashStoreImpl::PrefHashStoreTransactionImpl::
~PrefHashStoreTransactionImpl()114     ~PrefHashStoreTransactionImpl() {
115   if (super_mac_dirty_ && outer_->use_super_mac_) {
116     // Get the dictionary of hashes (or NULL if it doesn't exist).
117     const base::DictionaryValue* hashes_dict = contents()->GetContents();
118     contents()->SetSuperMac(outer_->pref_hash_calculator_.Calculate(
119         contents()->hash_store_id(), hashes_dict));
120   }
121 }
122 
123 PrefHashStoreTransaction::ValueState
CheckValue(const std::string & path,const base::Value * initial_value) const124 PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckValue(
125     const std::string& path,
126     const base::Value* initial_value) const {
127   const base::DictionaryValue* hashes_dict = contents()->GetContents();
128 
129   std::string last_hash;
130   if (hashes_dict)
131     hashes_dict->GetString(path, &last_hash);
132 
133   if (last_hash.empty()) {
134     // In the absence of a hash for this pref, always trust a NULL value, but
135     // only trust an existing value if the initial hashes dictionary is trusted.
136     return (!initial_value || super_mac_valid_) ? TRUSTED_UNKNOWN_VALUE
137                                                 : UNTRUSTED_UNKNOWN_VALUE;
138   }
139 
140   PrefHashCalculator::ValidationResult validation_result =
141       outer_->pref_hash_calculator_.Validate(path, initial_value, last_hash);
142   switch (validation_result) {
143     case PrefHashCalculator::VALID:
144       return UNCHANGED;
145     case PrefHashCalculator::VALID_WEAK_LEGACY:
146       return WEAK_LEGACY;
147     case PrefHashCalculator::VALID_SECURE_LEGACY:
148       return SECURE_LEGACY;
149     case PrefHashCalculator::INVALID:
150       return initial_value ? CHANGED : CLEARED;
151   }
152   NOTREACHED() << "Unexpected PrefHashCalculator::ValidationResult: "
153                << validation_result;
154   return UNTRUSTED_UNKNOWN_VALUE;
155 }
156 
StoreHash(const std::string & path,const base::Value * new_value)157 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreHash(
158     const std::string& path,
159     const base::Value* new_value) {
160   const std::string mac =
161       outer_->pref_hash_calculator_.Calculate(path, new_value);
162   (*contents()->GetMutableContents())->SetString(path, mac);
163   super_mac_dirty_ = true;
164 }
165 
166 PrefHashStoreTransaction::ValueState
CheckSplitValue(const std::string & path,const base::DictionaryValue * initial_split_value,std::vector<std::string> * invalid_keys) const167 PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckSplitValue(
168     const std::string& path,
169     const base::DictionaryValue* initial_split_value,
170     std::vector<std::string>* invalid_keys) const {
171   DCHECK(invalid_keys && invalid_keys->empty());
172 
173   std::map<std::string, std::string> split_macs;
174   const bool has_hashes = GetSplitMacs(path, &split_macs);
175 
176   // Treat NULL and empty the same; otherwise we would need to store a hash
177   // for the entire dictionary (or some other special beacon) to
178   // differentiate these two cases which are really the same for
179   // dictionaries.
180   if (!initial_split_value || initial_split_value->empty())
181     return has_hashes ? CLEARED : UNCHANGED;
182 
183   if (!has_hashes)
184     return super_mac_valid_ ? TRUSTED_UNKNOWN_VALUE : UNTRUSTED_UNKNOWN_VALUE;
185 
186   bool has_secure_legacy_id_hashes = false;
187   std::string keyed_path(path);
188   keyed_path.push_back('.');
189   const size_t common_part_length = keyed_path.length();
190   for (base::DictionaryValue::Iterator it(*initial_split_value); !it.IsAtEnd();
191        it.Advance()) {
192     std::map<std::string, std::string>::iterator entry =
193         split_macs.find(it.key());
194     if (entry == split_macs.end()) {
195       invalid_keys->push_back(it.key());
196     } else {
197       // Keep the common part from the old |keyed_path| and replace the key to
198       // get the new |keyed_path|.
199       keyed_path.replace(common_part_length, std::string::npos, it.key());
200       switch (outer_->pref_hash_calculator_.Validate(
201           keyed_path, &it.value(), entry->second)) {
202         case PrefHashCalculator::VALID:
203           break;
204         case WEAK_LEGACY:
205           // Split tracked preferences were introduced after the migration from
206           // the weaker legacy algorithm started so no migration is expected,
207           // but declare it invalid in Release builds anyways.
208           NOTREACHED();
209           invalid_keys->push_back(it.key());
210           break;
211         case SECURE_LEGACY:
212           // Secure legacy device IDs based hashes are still accepted, but we
213           // should make sure to notify the caller for him to update the legacy
214           // hashes.
215           has_secure_legacy_id_hashes = true;
216           break;
217         case PrefHashCalculator::INVALID:
218           invalid_keys->push_back(it.key());
219           break;
220       }
221       // Remove processed MACs, remaining MACs at the end will also be
222       // considered invalid.
223       split_macs.erase(entry);
224     }
225   }
226 
227   // Anything left in the map is missing from the data.
228   for (std::map<std::string, std::string>::const_iterator it =
229            split_macs.begin();
230        it != split_macs.end();
231        ++it) {
232     invalid_keys->push_back(it->first);
233   }
234 
235   return invalid_keys->empty()
236              ? (has_secure_legacy_id_hashes ? SECURE_LEGACY : UNCHANGED)
237              : CHANGED;
238 }
239 
StoreSplitHash(const std::string & path,const base::DictionaryValue * split_value)240 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreSplitHash(
241     const std::string& path,
242     const base::DictionaryValue* split_value) {
243   scoped_ptr<HashStoreContents::MutableDictionary> mutable_dictionary =
244       contents()->GetMutableContents();
245   (*mutable_dictionary)->Remove(path, NULL);
246 
247   if (split_value) {
248     std::string keyed_path(path);
249     keyed_path.push_back('.');
250     const size_t common_part_length = keyed_path.length();
251     for (base::DictionaryValue::Iterator it(*split_value); !it.IsAtEnd();
252          it.Advance()) {
253       // Keep the common part from the old |keyed_path| and replace the key to
254       // get the new |keyed_path|.
255       keyed_path.replace(common_part_length, std::string::npos, it.key());
256       (*mutable_dictionary)->SetString(
257           keyed_path,
258           outer_->pref_hash_calculator_.Calculate(keyed_path, &it.value()));
259     }
260   }
261   super_mac_dirty_ = true;
262 }
263 
GetSplitMacs(const std::string & key,std::map<std::string,std::string> * split_macs) const264 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::GetSplitMacs(
265     const std::string& key,
266     std::map<std::string, std::string>* split_macs) const {
267   DCHECK(split_macs);
268   DCHECK(split_macs->empty());
269 
270   const base::DictionaryValue* hashes_dict = contents()->GetContents();
271   const base::DictionaryValue* split_mac_dictionary = NULL;
272   if (!hashes_dict || !hashes_dict->GetDictionary(key, &split_mac_dictionary))
273     return false;
274   for (base::DictionaryValue::Iterator it(*split_mac_dictionary); !it.IsAtEnd();
275        it.Advance()) {
276     std::string mac_string;
277     if (!it.value().GetAsString(&mac_string)) {
278       NOTREACHED();
279       continue;
280     }
281     split_macs->insert(make_pair(it.key(), mac_string));
282   }
283   return true;
284 }
285 
HasHash(const std::string & path) const286 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::HasHash(
287     const std::string& path) const {
288   const base::DictionaryValue* hashes_dict = contents()->GetContents();
289   return hashes_dict && hashes_dict->Get(path, NULL);
290 }
291 
ImportHash(const std::string & path,const base::Value * hash)292 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ImportHash(
293     const std::string& path,
294     const base::Value* hash) {
295   DCHECK(hash);
296 
297   (*contents()->GetMutableContents())->Set(path, hash->DeepCopy());
298 
299   if (super_mac_valid_)
300     super_mac_dirty_ = true;
301 }
302 
ClearHash(const std::string & path)303 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ClearHash(
304     const std::string& path) {
305   if ((*contents()->GetMutableContents())->RemovePath(path, NULL) &&
306       super_mac_valid_) {
307     super_mac_dirty_ = true;
308   }
309 }
310 
IsSuperMACValid() const311 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::IsSuperMACValid() const {
312   return super_mac_valid_;
313 }
314 
StampSuperMac()315 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::StampSuperMac() {
316   if (!outer_->use_super_mac_ || super_mac_valid_)
317     return false;
318   super_mac_dirty_ = true;
319   return true;
320 }
321