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 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 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 73 PrefHashStoreImpl::~PrefHashStoreImpl() { 74 } 75 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 81 scoped_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction( 82 scoped_ptr<HashStoreContents> storage) { 83 return scoped_ptr<PrefHashStoreTransaction>( 84 new PrefHashStoreTransactionImpl(this, storage.Pass())); 85 } 86 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:: 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 124 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 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 167 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 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 264 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 286 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 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 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 311 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::IsSuperMACValid() const { 312 return super_mac_valid_; 313 } 314 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