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 13class 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 45 HashStoreContents* contents() { 46 return outer_->legacy_hash_store_contents_ 47 ? outer_->legacy_hash_store_contents_.get() 48 : contents_.get(); 49 } 50 51 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 66PrefHashStoreImpl::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 73PrefHashStoreImpl::~PrefHashStoreImpl() { 74} 75 76void 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 81scoped_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction( 82 scoped_ptr<HashStoreContents> storage) { 83 return scoped_ptr<PrefHashStoreTransaction>( 84 new PrefHashStoreTransactionImpl(this, storage.Pass())); 85} 86 87PrefHashStoreImpl::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 113PrefHashStoreImpl::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 123PrefHashStoreTransaction::ValueState 124PrefHashStoreImpl::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_SECURE_LEGACY: 146 return SECURE_LEGACY; 147 case PrefHashCalculator::INVALID: 148 return initial_value ? CHANGED : CLEARED; 149 } 150 NOTREACHED() << "Unexpected PrefHashCalculator::ValidationResult: " 151 << validation_result; 152 return UNTRUSTED_UNKNOWN_VALUE; 153} 154 155void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreHash( 156 const std::string& path, 157 const base::Value* new_value) { 158 const std::string mac = 159 outer_->pref_hash_calculator_.Calculate(path, new_value); 160 (*contents()->GetMutableContents())->SetString(path, mac); 161 super_mac_dirty_ = true; 162} 163 164PrefHashStoreTransaction::ValueState 165PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckSplitValue( 166 const std::string& path, 167 const base::DictionaryValue* initial_split_value, 168 std::vector<std::string>* invalid_keys) const { 169 DCHECK(invalid_keys && invalid_keys->empty()); 170 171 std::map<std::string, std::string> split_macs; 172 const bool has_hashes = GetSplitMacs(path, &split_macs); 173 174 // Treat NULL and empty the same; otherwise we would need to store a hash 175 // for the entire dictionary (or some other special beacon) to 176 // differentiate these two cases which are really the same for 177 // dictionaries. 178 if (!initial_split_value || initial_split_value->empty()) 179 return has_hashes ? CLEARED : UNCHANGED; 180 181 if (!has_hashes) 182 return super_mac_valid_ ? TRUSTED_UNKNOWN_VALUE : UNTRUSTED_UNKNOWN_VALUE; 183 184 bool has_secure_legacy_id_hashes = false; 185 std::string keyed_path(path); 186 keyed_path.push_back('.'); 187 const size_t common_part_length = keyed_path.length(); 188 for (base::DictionaryValue::Iterator it(*initial_split_value); !it.IsAtEnd(); 189 it.Advance()) { 190 std::map<std::string, std::string>::iterator entry = 191 split_macs.find(it.key()); 192 if (entry == split_macs.end()) { 193 invalid_keys->push_back(it.key()); 194 } else { 195 // Keep the common part from the old |keyed_path| and replace the key to 196 // get the new |keyed_path|. 197 keyed_path.replace(common_part_length, std::string::npos, it.key()); 198 switch (outer_->pref_hash_calculator_.Validate( 199 keyed_path, &it.value(), entry->second)) { 200 case PrefHashCalculator::VALID: 201 break; 202 case SECURE_LEGACY: 203 // Secure legacy device IDs based hashes are still accepted, but we 204 // should make sure to notify the caller for him to update the legacy 205 // hashes. 206 has_secure_legacy_id_hashes = true; 207 break; 208 case PrefHashCalculator::INVALID: 209 invalid_keys->push_back(it.key()); 210 break; 211 } 212 // Remove processed MACs, remaining MACs at the end will also be 213 // considered invalid. 214 split_macs.erase(entry); 215 } 216 } 217 218 // Anything left in the map is missing from the data. 219 for (std::map<std::string, std::string>::const_iterator it = 220 split_macs.begin(); 221 it != split_macs.end(); 222 ++it) { 223 invalid_keys->push_back(it->first); 224 } 225 226 return invalid_keys->empty() 227 ? (has_secure_legacy_id_hashes ? SECURE_LEGACY : UNCHANGED) 228 : CHANGED; 229} 230 231void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreSplitHash( 232 const std::string& path, 233 const base::DictionaryValue* split_value) { 234 scoped_ptr<HashStoreContents::MutableDictionary> mutable_dictionary = 235 contents()->GetMutableContents(); 236 (*mutable_dictionary)->Remove(path, NULL); 237 238 if (split_value) { 239 std::string keyed_path(path); 240 keyed_path.push_back('.'); 241 const size_t common_part_length = keyed_path.length(); 242 for (base::DictionaryValue::Iterator it(*split_value); !it.IsAtEnd(); 243 it.Advance()) { 244 // Keep the common part from the old |keyed_path| and replace the key to 245 // get the new |keyed_path|. 246 keyed_path.replace(common_part_length, std::string::npos, it.key()); 247 (*mutable_dictionary)->SetString( 248 keyed_path, 249 outer_->pref_hash_calculator_.Calculate(keyed_path, &it.value())); 250 } 251 } 252 super_mac_dirty_ = true; 253} 254 255bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::GetSplitMacs( 256 const std::string& key, 257 std::map<std::string, std::string>* split_macs) const { 258 DCHECK(split_macs); 259 DCHECK(split_macs->empty()); 260 261 const base::DictionaryValue* hashes_dict = contents()->GetContents(); 262 const base::DictionaryValue* split_mac_dictionary = NULL; 263 if (!hashes_dict || !hashes_dict->GetDictionary(key, &split_mac_dictionary)) 264 return false; 265 for (base::DictionaryValue::Iterator it(*split_mac_dictionary); !it.IsAtEnd(); 266 it.Advance()) { 267 std::string mac_string; 268 if (!it.value().GetAsString(&mac_string)) { 269 NOTREACHED(); 270 continue; 271 } 272 split_macs->insert(make_pair(it.key(), mac_string)); 273 } 274 return true; 275} 276 277bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::HasHash( 278 const std::string& path) const { 279 const base::DictionaryValue* hashes_dict = contents()->GetContents(); 280 return hashes_dict && hashes_dict->Get(path, NULL); 281} 282 283void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ImportHash( 284 const std::string& path, 285 const base::Value* hash) { 286 DCHECK(hash); 287 288 (*contents()->GetMutableContents())->Set(path, hash->DeepCopy()); 289 290 if (super_mac_valid_) 291 super_mac_dirty_ = true; 292} 293 294void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ClearHash( 295 const std::string& path) { 296 if ((*contents()->GetMutableContents())->RemovePath(path, NULL) && 297 super_mac_valid_) { 298 super_mac_dirty_ = true; 299 } 300} 301 302bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::IsSuperMACValid() const { 303 return super_mac_valid_; 304} 305 306bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::StampSuperMac() { 307 if (!outer_->use_super_mac_ || super_mac_valid_) 308 return false; 309 super_mac_dirty_ = true; 310 return true; 311} 312