| 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 |