OLD | NEW |
| (Empty) |
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/user_prefs/tracked/pref_hash_store_impl.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <utility> | |
9 | |
10 #include "base/logging.h" | |
11 #include "base/macros.h" | |
12 #include "base/metrics/histogram_macros.h" | |
13 #include "components/user_prefs/tracked/device_id.h" | |
14 #include "components/user_prefs/tracked/hash_store_contents.h" | |
15 | |
16 namespace { | |
17 | |
18 // Returns a deterministic ID for this machine. | |
19 std::string GenerateDeviceId() { | |
20 static std::string cached_device_id; | |
21 if (!cached_device_id.empty()) | |
22 return cached_device_id; | |
23 | |
24 std::string device_id; | |
25 MachineIdStatus status = GetDeterministicMachineSpecificId(&device_id); | |
26 if (status != MachineIdStatus::NOT_IMPLEMENTED) { | |
27 // TODO(proberge): Remove this histogram once we validate that machine id | |
28 // generation is not flaky and consider adding a CHECK or DCHECK. | |
29 UMA_HISTOGRAM_BOOLEAN("Settings.MachineIdGenerationSuccess", | |
30 status == MachineIdStatus::SUCCESS); | |
31 } | |
32 | |
33 if (status == MachineIdStatus::SUCCESS) { | |
34 cached_device_id = device_id; | |
35 return device_id; | |
36 } | |
37 | |
38 return std::string(); | |
39 } | |
40 | |
41 } // namespace | |
42 | |
43 class PrefHashStoreImpl::PrefHashStoreTransactionImpl | |
44 : public PrefHashStoreTransaction { | |
45 public: | |
46 // Constructs a PrefHashStoreTransactionImpl which can use the private | |
47 // members of its |outer| PrefHashStoreImpl. | |
48 PrefHashStoreTransactionImpl(PrefHashStoreImpl* outer, | |
49 HashStoreContents* storage); | |
50 ~PrefHashStoreTransactionImpl() override; | |
51 | |
52 // PrefHashStoreTransaction implementation. | |
53 base::StringPiece GetStoreUMASuffix() const override; | |
54 ValueState CheckValue(const std::string& path, | |
55 const base::Value* value) const override; | |
56 void StoreHash(const std::string& path, const base::Value* value) override; | |
57 ValueState CheckSplitValue( | |
58 const std::string& path, | |
59 const base::DictionaryValue* initial_split_value, | |
60 std::vector<std::string>* invalid_keys) const override; | |
61 void StoreSplitHash(const std::string& path, | |
62 const base::DictionaryValue* split_value) override; | |
63 bool HasHash(const std::string& path) const override; | |
64 void ImportHash(const std::string& path, const base::Value* hash) override; | |
65 void ClearHash(const std::string& path) override; | |
66 bool IsSuperMACValid() const override; | |
67 bool StampSuperMac() override; | |
68 | |
69 private: | |
70 PrefHashStoreImpl* outer_; | |
71 HashStoreContents* contents_; | |
72 | |
73 bool super_mac_valid_; | |
74 bool super_mac_dirty_; | |
75 | |
76 DISALLOW_COPY_AND_ASSIGN(PrefHashStoreTransactionImpl); | |
77 }; | |
78 | |
79 PrefHashStoreImpl::PrefHashStoreImpl(const std::string& seed, | |
80 const std::string& legacy_device_id, | |
81 bool use_super_mac) | |
82 : pref_hash_calculator_(seed, GenerateDeviceId(), legacy_device_id), | |
83 use_super_mac_(use_super_mac) {} | |
84 | |
85 PrefHashStoreImpl::~PrefHashStoreImpl() { | |
86 } | |
87 | |
88 std::unique_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction( | |
89 HashStoreContents* storage) { | |
90 return std::unique_ptr<PrefHashStoreTransaction>( | |
91 new PrefHashStoreTransactionImpl(this, std::move(storage))); | |
92 } | |
93 | |
94 std::string PrefHashStoreImpl::ComputeMac(const std::string& path, | |
95 const base::Value* value) { | |
96 return pref_hash_calculator_.Calculate(path, value); | |
97 } | |
98 | |
99 std::unique_ptr<base::DictionaryValue> PrefHashStoreImpl::ComputeSplitMacs( | |
100 const std::string& path, | |
101 const base::DictionaryValue* split_values) { | |
102 DCHECK(split_values); | |
103 | |
104 std::string keyed_path(path); | |
105 keyed_path.push_back('.'); | |
106 const size_t common_part_length = keyed_path.length(); | |
107 | |
108 std::unique_ptr<base::DictionaryValue> split_macs(new base::DictionaryValue); | |
109 | |
110 for (base::DictionaryValue::Iterator it(*split_values); !it.IsAtEnd(); | |
111 it.Advance()) { | |
112 // Keep the common part from the old |keyed_path| and replace the key to | |
113 // get the new |keyed_path|. | |
114 keyed_path.replace(common_part_length, std::string::npos, it.key()); | |
115 | |
116 split_macs->SetStringWithoutPathExpansion( | |
117 it.key(), ComputeMac(keyed_path, &it.value())); | |
118 } | |
119 | |
120 return split_macs; | |
121 } | |
122 | |
123 PrefHashStoreImpl::PrefHashStoreTransactionImpl::PrefHashStoreTransactionImpl( | |
124 PrefHashStoreImpl* outer, | |
125 HashStoreContents* storage) | |
126 : outer_(outer), | |
127 contents_(std::move(storage)), | |
128 super_mac_valid_(false), | |
129 super_mac_dirty_(false) { | |
130 if (!outer_->use_super_mac_) | |
131 return; | |
132 | |
133 // The store must have a valid super MAC to be trusted. | |
134 std::string super_mac = contents_->GetSuperMac(); | |
135 if (super_mac.empty()) | |
136 return; | |
137 | |
138 super_mac_valid_ = | |
139 outer_->pref_hash_calculator_.Validate( | |
140 "", contents_->GetContents(), super_mac) == PrefHashCalculator::VALID; | |
141 } | |
142 | |
143 PrefHashStoreImpl::PrefHashStoreTransactionImpl:: | |
144 ~PrefHashStoreTransactionImpl() { | |
145 if (super_mac_dirty_ && outer_->use_super_mac_) { | |
146 // Get the dictionary of hashes (or NULL if it doesn't exist). | |
147 const base::DictionaryValue* hashes_dict = contents_->GetContents(); | |
148 contents_->SetSuperMac(outer_->ComputeMac("", hashes_dict)); | |
149 } | |
150 } | |
151 | |
152 base::StringPiece | |
153 PrefHashStoreImpl::PrefHashStoreTransactionImpl::GetStoreUMASuffix() const { | |
154 return contents_->GetUMASuffix(); | |
155 } | |
156 | |
157 PrefHashStoreTransaction::ValueState | |
158 PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckValue( | |
159 const std::string& path, | |
160 const base::Value* initial_value) const { | |
161 std::string last_hash; | |
162 contents_->GetMac(path, &last_hash); | |
163 | |
164 if (last_hash.empty()) { | |
165 // In the absence of a hash for this pref, always trust a NULL value, but | |
166 // only trust an existing value if the initial hashes dictionary is trusted. | |
167 if (!initial_value) | |
168 return TRUSTED_NULL_VALUE; | |
169 else if (super_mac_valid_) | |
170 return TRUSTED_UNKNOWN_VALUE; | |
171 else | |
172 return UNTRUSTED_UNKNOWN_VALUE; | |
173 } | |
174 | |
175 PrefHashCalculator::ValidationResult validation_result = | |
176 outer_->pref_hash_calculator_.Validate(path, initial_value, last_hash); | |
177 switch (validation_result) { | |
178 case PrefHashCalculator::VALID: | |
179 return UNCHANGED; | |
180 case PrefHashCalculator::VALID_SECURE_LEGACY: | |
181 return SECURE_LEGACY; | |
182 case PrefHashCalculator::INVALID: | |
183 return initial_value ? CHANGED : CLEARED; | |
184 } | |
185 NOTREACHED() << "Unexpected PrefHashCalculator::ValidationResult: " | |
186 << validation_result; | |
187 return UNTRUSTED_UNKNOWN_VALUE; | |
188 } | |
189 | |
190 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreHash( | |
191 const std::string& path, | |
192 const base::Value* new_value) { | |
193 const std::string mac = outer_->ComputeMac(path, new_value); | |
194 contents_->SetMac(path, mac); | |
195 super_mac_dirty_ = true; | |
196 } | |
197 | |
198 PrefHashStoreTransaction::ValueState | |
199 PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckSplitValue( | |
200 const std::string& path, | |
201 const base::DictionaryValue* initial_split_value, | |
202 std::vector<std::string>* invalid_keys) const { | |
203 DCHECK(invalid_keys && invalid_keys->empty()); | |
204 | |
205 std::map<std::string, std::string> split_macs; | |
206 const bool has_hashes = contents_->GetSplitMacs(path, &split_macs); | |
207 | |
208 // Treat NULL and empty the same; otherwise we would need to store a hash for | |
209 // the entire dictionary (or some other special beacon) to differentiate these | |
210 // two cases which are really the same for dictionaries. | |
211 if (!initial_split_value || initial_split_value->empty()) | |
212 return has_hashes ? CLEARED : UNCHANGED; | |
213 | |
214 if (!has_hashes) | |
215 return super_mac_valid_ ? TRUSTED_UNKNOWN_VALUE : UNTRUSTED_UNKNOWN_VALUE; | |
216 | |
217 bool has_secure_legacy_id_hashes = false; | |
218 std::string keyed_path(path); | |
219 keyed_path.push_back('.'); | |
220 const size_t common_part_length = keyed_path.length(); | |
221 for (base::DictionaryValue::Iterator it(*initial_split_value); !it.IsAtEnd(); | |
222 it.Advance()) { | |
223 std::map<std::string, std::string>::iterator entry = | |
224 split_macs.find(it.key()); | |
225 if (entry == split_macs.end()) { | |
226 invalid_keys->push_back(it.key()); | |
227 } else { | |
228 // Keep the common part from the old |keyed_path| and replace the key to | |
229 // get the new |keyed_path|. | |
230 keyed_path.replace(common_part_length, std::string::npos, it.key()); | |
231 switch (outer_->pref_hash_calculator_.Validate(keyed_path, &it.value(), | |
232 entry->second)) { | |
233 case PrefHashCalculator::VALID: | |
234 break; | |
235 case SECURE_LEGACY: | |
236 // Secure legacy device IDs based hashes are still accepted, but we | |
237 // should make sure to notify the caller for them to update the legacy | |
238 // hashes. | |
239 has_secure_legacy_id_hashes = true; | |
240 break; | |
241 case PrefHashCalculator::INVALID: | |
242 invalid_keys->push_back(it.key()); | |
243 break; | |
244 } | |
245 // Remove processed MACs, remaining MACs at the end will also be | |
246 // considered invalid. | |
247 split_macs.erase(entry); | |
248 } | |
249 } | |
250 | |
251 // Anything left in the map is missing from the data. | |
252 for (std::map<std::string, std::string>::const_iterator it = | |
253 split_macs.begin(); | |
254 it != split_macs.end(); ++it) { | |
255 invalid_keys->push_back(it->first); | |
256 } | |
257 | |
258 return invalid_keys->empty() | |
259 ? (has_secure_legacy_id_hashes ? SECURE_LEGACY : UNCHANGED) | |
260 : CHANGED; | |
261 } | |
262 | |
263 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreSplitHash( | |
264 const std::string& path, | |
265 const base::DictionaryValue* split_value) { | |
266 contents_->RemoveEntry(path); | |
267 | |
268 if (split_value) { | |
269 std::unique_ptr<base::DictionaryValue> split_macs = | |
270 outer_->ComputeSplitMacs(path, split_value); | |
271 | |
272 for (base::DictionaryValue::Iterator it(*split_macs); !it.IsAtEnd(); | |
273 it.Advance()) { | |
274 const base::Value* value_as_string; | |
275 bool is_string = it.value().GetAsString(&value_as_string); | |
276 DCHECK(is_string); | |
277 | |
278 contents_->SetSplitMac(path, it.key(), value_as_string->GetString()); | |
279 } | |
280 } | |
281 super_mac_dirty_ = true; | |
282 } | |
283 | |
284 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::HasHash( | |
285 const std::string& path) const { | |
286 std::string out_value; | |
287 std::map<std::string, std::string> out_values; | |
288 return contents_->GetMac(path, &out_value) || | |
289 contents_->GetSplitMacs(path, &out_values); | |
290 } | |
291 | |
292 void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ImportHash( | |
293 const std::string& path, | |
294 const base::Value* hash) { | |
295 DCHECK(hash); | |
296 | |
297 contents_->ImportEntry(path, hash); | |
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_->RemoveEntry(path) && super_mac_valid_) { | |
306 super_mac_dirty_ = true; | |
307 } | |
308 } | |
309 | |
310 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::IsSuperMACValid() const { | |
311 return super_mac_valid_; | |
312 } | |
313 | |
314 bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::StampSuperMac() { | |
315 if (!outer_->use_super_mac_ || super_mac_valid_) | |
316 return false; | |
317 super_mac_dirty_ = true; | |
318 return true; | |
319 } | |
OLD | NEW |