OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/protector/protected_prefs_watcher.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/bind.h" | |
9 #include "base/logging.h" | |
10 #include "base/metrics/histogram.h" | |
11 #include "base/stringprintf.h" | |
12 #include "base/values.h" | |
13 #include "chrome/browser/extensions/extension_prefs.h" | |
14 #include "chrome/browser/prefs/pref_service.h" | |
15 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
16 #include "chrome/browser/prefs/session_startup_pref.h" | |
17 #include "chrome/browser/profiles/profile.h" | |
18 #include "chrome/browser/protector/histograms.h" | |
19 #include "chrome/browser/protector/protector_utils.h" | |
20 #include "chrome/common/chrome_notification_types.h" | |
21 #include "chrome/common/pref_names.h" | |
22 #include "content/public/browser/notification_service.h" | |
23 | |
24 using extensions::ExtensionPrefs; | |
25 | |
26 namespace protector { | |
27 | |
28 namespace { | |
29 | |
30 // Prefix added to names of backup entries. | |
31 const char kBackupPrefsPrefix[] = "backup."; | |
32 | |
33 // Names of prefs that are backed up. | |
34 const char* const kProtectedPrefNames[] = { | |
35 prefs::kHomePage, | |
36 prefs::kHomePageIsNewTabPage, | |
37 prefs::kShowHomeButton, | |
38 prefs::kRestoreOnStartup, | |
39 prefs::kURLsToRestoreOnStartup, | |
40 prefs::kPinnedTabs | |
41 }; | |
42 | |
43 // Backup pref names. | |
44 const char kBackupHomePage[] = "backup.homepage"; | |
45 const char kBackupHomePageIsNewTabPage[] = "backup.homepage_is_newtabpage"; | |
46 const char kBackupShowHomeButton[] = "backup.browser.show_home_button"; | |
47 const char kBackupRestoreOnStartup[] = "backup.session.restore_on_startup"; | |
48 const char kBackupURLsToRestoreOnStartup[] = | |
49 "backup.session.urls_to_restore_on_startup"; | |
50 const char kBackupPinnedTabs[] = "backup.pinned_tabs"; | |
51 | |
52 // Special backup entries. | |
53 const char kBackupExtensionsIDs[] = "backup.extensions.ids"; | |
54 const char kBackupSignature[] = "backup._signature"; | |
55 const char kBackupVersion[] = "backup._version"; | |
56 | |
57 // Returns name of the backup entry for pref |pref_name|. | |
58 std::string GetBackupNameFor(const std::string& pref_name) { | |
59 return kBackupPrefsPrefix + pref_name; | |
60 } | |
61 | |
62 // Appends a list of strings to |out|. | |
63 void StringAppendStringList(const base::ListValue* list, std::string* out) { | |
64 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); | |
65 ++it) { | |
66 std::string item; | |
67 if (!(*it)->GetAsString(&item)) | |
68 NOTREACHED(); | |
69 base::StringAppendF(out, "|%s", item.c_str()); | |
70 } | |
71 } | |
72 | |
73 // Appends a dictionary with string values to |out|. | |
74 void StringAppendStringDictionary(const base::DictionaryValue* dict, | |
75 std::string* out) { | |
76 for (base::DictionaryValue::Iterator it(*dict); it.HasNext(); it.Advance()) { | |
77 std::string value; | |
78 if (!it.value().GetAsString(&value)) | |
79 NOTREACHED(); | |
80 base::StringAppendF(out, "|%s|%s", it.key().c_str(), value.c_str()); | |
81 } | |
82 } | |
83 | |
84 void StringAppendBoolean(PrefService* prefs, | |
85 const char* path, | |
86 std::string* out) { | |
87 if (prefs->HasPrefPath(path)) | |
88 base::StringAppendF(out, "|%d", prefs->GetBoolean(path) ? 1 : 0); | |
89 else | |
90 base::StringAppendF(out, "|"); | |
91 } | |
92 | |
93 void StringAppendInteger(PrefService* prefs, | |
94 const char* path, | |
95 std::string* out) { | |
96 if (prefs->HasPrefPath(path)) | |
97 base::StringAppendF(out, "|%d", prefs->GetInteger(path)); | |
98 else | |
99 base::StringAppendF(out, "|"); | |
100 } | |
101 | |
102 } // namespace | |
103 | |
104 // static | |
105 const int ProtectedPrefsWatcher::kCurrentVersionNumber = 4; | |
106 | |
107 ProtectedPrefsWatcher::ProtectedPrefsWatcher(Profile* profile) | |
108 : is_backup_valid_(true), | |
109 profile_(profile) { | |
110 // Perform necessary pref migrations before actually starting to observe | |
111 // pref changes, otherwise the migration would affect the backup data as well. | |
112 EnsurePrefsMigration(); | |
113 | |
114 pref_observer_.Init(profile->GetPrefs()); | |
115 PrefChangeRegistrar::NamedChangeCallback callback = base::Bind( | |
116 &ProtectedPrefsWatcher::OnPreferenceChanged, base::Unretained(this)); | |
117 pref_observer_.Add(prefs::kHomePageIsNewTabPage, callback); | |
118 pref_observer_.Add(prefs::kHomePage, callback); | |
119 pref_observer_.Add(prefs::kShowHomeButton, callback); | |
120 // Session startup. | |
121 pref_observer_.Add(prefs::kRestoreOnStartup, callback); | |
122 pref_observer_.Add(prefs::kURLsToRestoreOnStartup, callback); | |
123 // Pinned tabs. | |
124 pref_observer_.Add(prefs::kPinnedTabs, callback); | |
125 // Extensions. | |
126 pref_observer_.Add(ExtensionPrefs::kExtensionsPref, callback); | |
127 | |
128 UpdateCachedPrefs(); | |
129 ValidateBackup(); | |
130 VLOG(1) << "Initialized pref watcher"; | |
131 } | |
132 | |
133 ProtectedPrefsWatcher::~ProtectedPrefsWatcher() { | |
134 } | |
135 | |
136 // static | |
137 void ProtectedPrefsWatcher::RegisterUserPrefs(PrefService* prefs) { | |
138 prefs->RegisterStringPref(kBackupHomePage, "", | |
139 PrefService::UNSYNCABLE_PREF); | |
140 prefs->RegisterBooleanPref(kBackupHomePageIsNewTabPage, false, | |
141 PrefService::UNSYNCABLE_PREF); | |
142 prefs->RegisterBooleanPref(kBackupShowHomeButton, false, | |
143 PrefService::UNSYNCABLE_PREF); | |
144 prefs->RegisterIntegerPref(kBackupRestoreOnStartup, 0, | |
145 PrefService::UNSYNCABLE_PREF); | |
146 prefs->RegisterListPref(kBackupURLsToRestoreOnStartup, | |
147 PrefService::UNSYNCABLE_PREF); | |
148 prefs->RegisterListPref(kBackupPinnedTabs, | |
149 PrefService::UNSYNCABLE_PREF); | |
150 prefs->RegisterListPref(kBackupExtensionsIDs, | |
151 PrefService::UNSYNCABLE_PREF); | |
152 prefs->RegisterStringPref(kBackupSignature, "", | |
153 PrefService::UNSYNCABLE_PREF); | |
154 prefs->RegisterIntegerPref(kBackupVersion, 1, | |
155 PrefService::UNSYNCABLE_PREF); | |
156 } | |
157 | |
158 bool ProtectedPrefsWatcher::DidPrefChange(const std::string& path) const { | |
159 std::string backup_path = GetBackupNameFor(path); | |
160 PrefService* prefs = profile_->GetPrefs(); | |
161 const PrefService::Preference* new_pref = prefs->FindPreference(path.c_str()); | |
162 DCHECK(new_pref); | |
163 const PrefService::Preference* backup_pref = | |
164 profile_->GetPrefs()->FindPreference(backup_path.c_str()); | |
165 DCHECK(backup_pref); | |
166 if (new_pref->IsDefaultValue()) | |
167 return !backup_pref->IsDefaultValue(); | |
168 if (!new_pref->IsUserControlled()) | |
169 return false; | |
170 return !backup_pref->GetValue()->Equals(new_pref->GetValue()); | |
171 } | |
172 | |
173 const base::Value* ProtectedPrefsWatcher::GetBackupForPref( | |
174 const std::string& path) const { | |
175 if (!is_backup_valid_) | |
176 return NULL; | |
177 std::string backup_path = GetBackupNameFor(path); | |
178 // These do not directly correspond to any real preference. | |
179 DCHECK(backup_path != kBackupExtensionsIDs && | |
180 backup_path != kBackupSignature); | |
181 PrefService* prefs = profile_->GetPrefs(); | |
182 // If backup is not set, return the default value of the actual pref. | |
183 // TODO(ivankr): return NULL instead and handle appropriately in SettingChange | |
184 // classes. | |
185 if (!prefs->HasPrefPath(backup_path.c_str())) | |
186 return prefs->GetDefaultPrefValue(path.c_str()); | |
187 const PrefService::Preference* backup_pref = | |
188 profile_->GetPrefs()->FindPreference(backup_path.c_str()); | |
189 DCHECK(backup_pref); | |
190 return backup_pref->GetValue(); | |
191 } | |
192 | |
193 void ProtectedPrefsWatcher::ForceUpdateBackup() { | |
194 UMA_HISTOGRAM_ENUMERATION( | |
195 kProtectorHistogramPrefs, | |
196 kProtectorErrorForcedUpdate, | |
197 kProtectorErrorCount); | |
198 InitBackup(); | |
199 } | |
200 | |
201 void ProtectedPrefsWatcher::OnPreferenceChanged(const std::string& pref_name) { | |
202 DCHECK(pref_observer_.IsObserved(pref_name)); | |
203 if (UpdateBackupEntry(pref_name)) | |
204 UpdateBackupSignature(); | |
205 } | |
206 | |
207 void ProtectedPrefsWatcher::EnsurePrefsMigration() { | |
208 SessionStartupPref::MigrateIfNecessary(profile_->GetPrefs()); | |
209 } | |
210 | |
211 bool ProtectedPrefsWatcher::UpdateCachedPrefs() { | |
212 // ExtensionService may not yet have been initialized, so using static method | |
213 // exposed for this purpose. | |
214 extensions::ExtensionIdList extension_ids = | |
215 ExtensionPrefs::GetExtensionsFrom(profile_->GetPrefs()); | |
216 if (extension_ids == cached_extension_ids_) | |
217 return false; | |
218 cached_extension_ids_.swap(extension_ids); | |
219 return true; | |
220 } | |
221 | |
222 bool ProtectedPrefsWatcher::HasBackup() const { | |
223 // TODO(ivankr): as soon as some irreversible change to Preferences happens, | |
224 // add a condition that this change has occured as well (otherwise it's | |
225 // possible to simply clear the "backup" dictionary to make settings | |
226 // unprotected). | |
227 return profile_->GetPrefs()->HasPrefPath(kBackupSignature); | |
228 } | |
229 | |
230 void ProtectedPrefsWatcher::InitBackup() { | |
231 PrefService* prefs = profile_->GetPrefs(); | |
232 for (size_t i = 0; i < arraysize(kProtectedPrefNames); ++i) { | |
233 const base::Value* user_value = | |
234 prefs->GetUserPrefValue(kProtectedPrefNames[i]); | |
235 if (user_value) | |
236 prefs->Set(GetBackupNameFor(kProtectedPrefNames[i]).c_str(), *user_value); | |
237 } | |
238 ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs); | |
239 base::ListValue* extension_ids = extension_ids_update.Get(); | |
240 extension_ids->Clear(); | |
241 for (extensions::ExtensionIdList::const_iterator it = | |
242 cached_extension_ids_.begin(); | |
243 it != cached_extension_ids_.end(); ++it) { | |
244 extension_ids->Append(base::Value::CreateStringValue(*it)); | |
245 } | |
246 prefs->SetInteger(kBackupVersion, kCurrentVersionNumber); | |
247 UpdateBackupSignature(); | |
248 } | |
249 | |
250 void ProtectedPrefsWatcher::MigrateOldBackupIfNeeded() { | |
251 PrefService* prefs = profile_->GetPrefs(); | |
252 | |
253 int current_version = prefs->GetInteger(kBackupVersion); | |
254 VLOG(1) << "Backup version: " << current_version; | |
255 if (current_version == kCurrentVersionNumber) | |
256 return; | |
257 | |
258 switch (current_version) { | |
259 case 1: { | |
260 // Add pinned tabs. | |
261 const base::Value* pinned_tabs = | |
262 prefs->GetUserPrefValue(prefs::kPinnedTabs); | |
263 if (pinned_tabs) | |
264 prefs->Set(kBackupPinnedTabs, *pinned_tabs); | |
265 } | |
266 // FALL THROUGH | |
267 | |
268 case 2: | |
269 // SessionStartupPref migration. | |
270 DCHECK(prefs->GetBoolean(prefs::kRestoreOnStartupMigrated)); | |
271 prefs->SetInteger(kBackupRestoreOnStartup, | |
272 prefs->GetInteger(prefs::kRestoreOnStartup)); | |
273 prefs->Set(kBackupURLsToRestoreOnStartup, | |
274 *prefs->GetList(prefs::kURLsToRestoreOnStartup)); | |
275 // FALL THROUGH | |
276 | |
277 case 3: | |
278 // Reset to default values backup prefs whose actual prefs are not set. | |
279 for (size_t i = 0; i < arraysize(kProtectedPrefNames); ++i) { | |
280 if (!prefs->HasPrefPath(kProtectedPrefNames[i])) | |
281 prefs->ClearPref(GetBackupNameFor(kProtectedPrefNames[i]).c_str()); | |
282 } | |
283 // FALL THROUGH | |
284 } | |
285 | |
286 prefs->SetInteger(kBackupVersion, kCurrentVersionNumber); | |
287 UpdateBackupSignature(); | |
288 } | |
289 | |
290 bool ProtectedPrefsWatcher::UpdateBackupEntry(const std::string& path) { | |
291 std::string backup_path = GetBackupNameFor(path); | |
292 PrefService* prefs = profile_->GetPrefs(); | |
293 const PrefService::Preference* pref = prefs->FindPreference(path.c_str()); | |
294 if (path == ExtensionPrefs::kExtensionsPref) { | |
295 // For changes in extension dictionary, do nothing if the IDs list remained | |
296 // the same. | |
297 if (!UpdateCachedPrefs()) | |
298 return false; | |
299 ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs); | |
300 base::ListValue* extension_ids = extension_ids_update.Get(); | |
301 extension_ids->Clear(); | |
302 for (extensions::ExtensionIdList::const_iterator it = | |
303 cached_extension_ids_.begin(); | |
304 it != cached_extension_ids_.end(); ++it) { | |
305 extension_ids->Append(base::Value::CreateStringValue(*it)); | |
306 } | |
307 } else if (!prefs->HasPrefPath(path.c_str())) { | |
308 // Preference has been removed, remove the backup as well. | |
309 prefs->ClearPref(backup_path.c_str()); | |
310 } else if (!pref->IsUserControlled()) { | |
311 return false; | |
312 } else { | |
313 prefs->Set(backup_path.c_str(), *pref->GetValue()); | |
314 } | |
315 VLOG(1) << "Updated backup entry for: " << path; | |
316 return true; | |
317 } | |
318 | |
319 void ProtectedPrefsWatcher::UpdateBackupSignature() { | |
320 PrefService* prefs = profile_->GetPrefs(); | |
321 std::string signed_data = GetSignatureData(prefs); | |
322 DCHECK(!signed_data.empty()); | |
323 std::string signature = SignSetting(signed_data); | |
324 DCHECK(!signature.empty()); | |
325 std::string signature_base64; | |
326 if (!base::Base64Encode(signature, &signature_base64)) | |
327 NOTREACHED(); | |
328 prefs->SetString(kBackupSignature, signature_base64); | |
329 // Schedule disk write on FILE thread as soon as possible. | |
330 prefs->CommitPendingWrite(); | |
331 VLOG(1) << "Updated backup signature"; | |
332 } | |
333 | |
334 bool ProtectedPrefsWatcher::IsSignatureValid() const { | |
335 DCHECK(HasBackup()); | |
336 PrefService* prefs = profile_->GetPrefs(); | |
337 std::string signed_data = GetSignatureData(prefs); | |
338 DCHECK(!signed_data.empty()); | |
339 std::string signature; | |
340 if (!base::Base64Decode(prefs->GetString(kBackupSignature), &signature)) | |
341 return false; | |
342 return IsSettingValid(signed_data, signature); | |
343 } | |
344 | |
345 void ProtectedPrefsWatcher::ValidateBackup() { | |
346 if (!HasBackup()) { | |
347 // Create initial backup entries and sign them. | |
348 InitBackup(); | |
349 UMA_HISTOGRAM_ENUMERATION( | |
350 kProtectorHistogramPrefs, | |
351 kProtectorErrorValueValidZero, | |
352 kProtectorErrorCount); | |
353 } else if (IsSignatureValid()) { | |
354 MigrateOldBackupIfNeeded(); | |
355 UMA_HISTOGRAM_ENUMERATION( | |
356 kProtectorHistogramPrefs, | |
357 kProtectorErrorValueValid, | |
358 kProtectorErrorCount); | |
359 } else { | |
360 LOG(WARNING) << "Invalid backup signature"; | |
361 is_backup_valid_ = false; | |
362 // The whole backup has been compromised, overwrite it. | |
363 InitBackup(); | |
364 UMA_HISTOGRAM_ENUMERATION( | |
365 kProtectorHistogramPrefs, | |
366 kProtectorErrorBackupInvalid, | |
367 kProtectorErrorCount); | |
368 } | |
369 } | |
370 | |
371 std::string ProtectedPrefsWatcher::GetSignatureData(PrefService* prefs) const { | |
372 int current_version = prefs->GetInteger(kBackupVersion); | |
373 // TODO(ivankr): replace this with some existing reliable serializer. | |
374 // JSONWriter isn't a good choice because JSON formatting may change suddenly. | |
375 std::string data = prefs->GetString(kBackupHomePage); | |
376 StringAppendBoolean(prefs, kBackupHomePageIsNewTabPage, &data); | |
377 StringAppendBoolean(prefs, kBackupShowHomeButton, &data); | |
378 StringAppendInteger(prefs, kBackupRestoreOnStartup, &data); | |
379 StringAppendStringList(prefs->GetList(kBackupURLsToRestoreOnStartup), &data); | |
380 StringAppendStringList(prefs->GetList(kBackupExtensionsIDs), &data); | |
381 if (current_version >= 2) { | |
382 // Version itself is included only since version 2 since it wasn't there | |
383 // in version 1. | |
384 base::StringAppendF(&data, "|v%d", current_version); | |
385 const base::ListValue* pinned_tabs = prefs->GetList(kBackupPinnedTabs); | |
386 for (base::ListValue::const_iterator it = pinned_tabs->begin(); | |
387 it != pinned_tabs->end(); ++it) { | |
388 const base::DictionaryValue* tab = NULL; | |
389 if (!(*it)->GetAsDictionary(&tab)) { | |
390 NOTREACHED(); | |
391 continue; | |
392 } | |
393 StringAppendStringDictionary(tab, &data); | |
394 } | |
395 } | |
396 return data; | |
397 } | |
398 | |
399 } // namespace protector | |
OLD | NEW |