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/logging.h" |
| 9 #include "base/metrics/histogram.h" |
| 10 #include "base/stringprintf.h" |
| 11 #include "base/values.h" |
| 12 #include "chrome/browser/prefs/pref_service.h" |
| 13 #include "chrome/browser/prefs/pref_set_observer.h" |
| 14 #include "chrome/browser/prefs/scoped_user_pref_update.h" |
| 15 #include "chrome/browser/prefs/session_startup_pref.h" |
| 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/protector/histograms.h" |
| 18 #include "chrome/browser/protector/protector_service.h" |
| 19 #include "chrome/common/chrome_notification_types.h" |
| 20 #include "chrome/common/pref_names.h" |
| 21 #include "content/public/browser/notification_service.h" |
| 22 |
| 23 namespace protector { |
| 24 |
| 25 namespace { |
| 26 |
| 27 const char kBackupPrefsPrefix[] = "backup."; |
| 28 |
| 29 // Backup pref names. |
| 30 const char kBackupHomePage[] = "backup.homepage"; |
| 31 const char kBackupHomePageIsNewTabPage[] = "backup.homepage_is_newtabpage"; |
| 32 const char kBackupShowHomeButton[] = "backup.browser.show_home_button"; |
| 33 const char kBackupRestoreOnStartup[] = "backup.session.restore_on_startup"; |
| 34 const char kBackupURLsToRestoreOnStartup[] = |
| 35 "backup.session.urls_to_restore_on_startup"; |
| 36 const char kBackupExtensionsIDs[] = "backup.extensions.ids"; |
| 37 const char kBackupSignature[] = "backup._signature"; |
| 38 |
| 39 } // namespace |
| 40 |
| 41 ProtectedPrefsWatcher::ProtectedPrefsWatcher(Profile* profile) |
| 42 : is_backup_valid_(true), |
| 43 profile_(profile) { |
| 44 // Perform necessary pref migrations before actually starting to observe |
| 45 // pref changes, otherwise the migration would affect the backup data as well. |
| 46 EnsurePrefsMigration(); |
| 47 pref_observer_.reset(PrefSetObserver::CreateProtectedPrefSetObserver( |
| 48 profile->GetPrefs(), this)); |
| 49 UpdateCachedPrefs(); |
| 50 ValidateBackup(); |
| 51 VLOG(1) << "Initialized pref watcher"; |
| 52 } |
| 53 |
| 54 ProtectedPrefsWatcher::~ProtectedPrefsWatcher() { |
| 55 } |
| 56 |
| 57 // static |
| 58 void ProtectedPrefsWatcher::RegisterUserPrefs(PrefService* prefs) { |
| 59 prefs->RegisterStringPref(kBackupHomePage, "", |
| 60 PrefService::UNSYNCABLE_PREF); |
| 61 prefs->RegisterBooleanPref(kBackupHomePageIsNewTabPage, false, |
| 62 PrefService::UNSYNCABLE_PREF); |
| 63 prefs->RegisterBooleanPref(kBackupShowHomeButton, false, |
| 64 PrefService::UNSYNCABLE_PREF); |
| 65 prefs->RegisterIntegerPref(kBackupRestoreOnStartup, 0, |
| 66 PrefService::UNSYNCABLE_PREF); |
| 67 prefs->RegisterListPref(kBackupURLsToRestoreOnStartup, |
| 68 PrefService::UNSYNCABLE_PREF); |
| 69 prefs->RegisterListPref(kBackupExtensionsIDs, |
| 70 PrefService::UNSYNCABLE_PREF); |
| 71 prefs->RegisterStringPref(kBackupSignature, "", |
| 72 PrefService::UNSYNCABLE_PREF); |
| 73 } |
| 74 |
| 75 const base::Value* ProtectedPrefsWatcher::GetBackupForPref( |
| 76 const std::string& path) const { |
| 77 if (!is_backup_valid_) |
| 78 return NULL; |
| 79 std::string backup_path = std::string(kBackupPrefsPrefix) + path; |
| 80 const PrefService::Preference* backup_pref = |
| 81 profile_->GetPrefs()->FindPreference(backup_path.c_str()); |
| 82 DCHECK(backup_pref && |
| 83 // These do not directly correspond to any real preference. |
| 84 backup_path != kBackupExtensionsIDs && |
| 85 backup_path != kBackupSignature); |
| 86 return backup_pref->GetValue(); |
| 87 } |
| 88 |
| 89 void ProtectedPrefsWatcher::Observe( |
| 90 int type, |
| 91 const content::NotificationSource& source, |
| 92 const content::NotificationDetails& details) { |
| 93 DCHECK(type == chrome::NOTIFICATION_PREF_CHANGED); |
| 94 const std::string* pref_name = content::Details<std::string>(details).ptr(); |
| 95 DCHECK(pref_name && pref_observer_->IsObserved(*pref_name)); |
| 96 if (UpdateBackupEntry(*pref_name)) |
| 97 UpdateBackupSignature(); |
| 98 } |
| 99 |
| 100 void ProtectedPrefsWatcher::EnsurePrefsMigration() { |
| 101 // Force SessionStartupPref migration, if necessary. |
| 102 SessionStartupPref::GetStartupPref(profile_); |
| 103 } |
| 104 |
| 105 bool ProtectedPrefsWatcher::UpdateCachedPrefs() { |
| 106 // Direct access to the extensions prefs is required becase ExtensionService |
| 107 // may not yet have been initialized. |
| 108 ExtensionPrefs::ExtensionIdSet extension_ids = |
| 109 ExtensionPrefs::GetExtensionsFrom( |
| 110 profile_->GetPrefs()->GetDictionary(ExtensionPrefs::kExtensionsPref)); |
| 111 if (extension_ids == cached_extension_ids_) |
| 112 return false; |
| 113 cached_extension_ids_.swap(extension_ids); |
| 114 return true; |
| 115 } |
| 116 |
| 117 bool ProtectedPrefsWatcher::HasBackup() const { |
| 118 // TODO(ivankr): as soon as some irreversible change to Preferences happens, |
| 119 // add a condition that this change has occured as well (otherwise it's |
| 120 // possible to simply clear the "backup" dictionary to make settings |
| 121 // unprotected). |
| 122 return profile_->GetPrefs()->HasPrefPath(kBackupSignature); |
| 123 } |
| 124 |
| 125 void ProtectedPrefsWatcher::InitBackup() { |
| 126 PrefService* prefs = profile_->GetPrefs(); |
| 127 prefs->SetString(kBackupHomePage, prefs->GetString(prefs::kHomePage)); |
| 128 prefs->SetBoolean(kBackupHomePageIsNewTabPage, |
| 129 prefs->GetBoolean(prefs::kHomePageIsNewTabPage)); |
| 130 prefs->SetBoolean(kBackupShowHomeButton, |
| 131 prefs->GetBoolean(prefs::kShowHomeButton)); |
| 132 prefs->SetInteger(kBackupRestoreOnStartup, |
| 133 prefs->GetInteger(prefs::kRestoreOnStartup)); |
| 134 prefs->Set(kBackupURLsToRestoreOnStartup, |
| 135 *prefs->GetList(prefs::kURLsToRestoreOnStartup)); |
| 136 ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs); |
| 137 base::ListValue* extension_ids = extension_ids_update.Get(); |
| 138 extension_ids->Clear(); |
| 139 for (ExtensionPrefs::ExtensionIdSet::const_iterator it = |
| 140 cached_extension_ids_.begin(); |
| 141 it != cached_extension_ids_.end(); ++it) { |
| 142 extension_ids->Append(base::Value::CreateStringValue(*it)); |
| 143 } |
| 144 UpdateBackupSignature(); |
| 145 } |
| 146 |
| 147 bool ProtectedPrefsWatcher::UpdateBackupEntry(const std::string& pref_name) { |
| 148 PrefService* prefs = profile_->GetPrefs(); |
| 149 if (pref_name == ExtensionPrefs::kExtensionsPref) { |
| 150 // For changes in extension dictionary, do nothing if the IDs list remained |
| 151 // the same. |
| 152 if (!UpdateCachedPrefs()) |
| 153 return false; |
| 154 ListPrefUpdate extension_ids_update(prefs, kBackupExtensionsIDs); |
| 155 base::ListValue* extension_ids = extension_ids_update.Get(); |
| 156 extension_ids->Clear(); |
| 157 for (ExtensionPrefs::ExtensionIdSet::const_iterator it = |
| 158 cached_extension_ids_.begin(); |
| 159 it != cached_extension_ids_.end(); ++it) { |
| 160 extension_ids->Append(base::Value::CreateStringValue(*it)); |
| 161 } |
| 162 } else if (pref_name == prefs::kHomePage) { |
| 163 prefs->SetString(kBackupHomePage, prefs->GetString(prefs::kHomePage)); |
| 164 } else if (pref_name == prefs::kHomePageIsNewTabPage) { |
| 165 prefs->SetBoolean(kBackupHomePageIsNewTabPage, |
| 166 prefs->GetBoolean(prefs::kHomePageIsNewTabPage)); |
| 167 } else if (pref_name == prefs::kShowHomeButton) { |
| 168 prefs->SetBoolean(kBackupShowHomeButton, |
| 169 prefs->GetBoolean(prefs::kShowHomeButton)); |
| 170 } else if (pref_name == prefs::kRestoreOnStartup) { |
| 171 prefs->SetInteger(kBackupRestoreOnStartup, |
| 172 prefs->GetInteger(prefs::kRestoreOnStartup)); |
| 173 } else if (pref_name == prefs::kURLsToRestoreOnStartup) { |
| 174 prefs->Set(kBackupURLsToRestoreOnStartup, |
| 175 *prefs->GetList(prefs::kURLsToRestoreOnStartup)); |
| 176 } else { |
| 177 NOTREACHED(); |
| 178 return false; |
| 179 } |
| 180 VLOG(1) << "Updated backup entry for: " << pref_name; |
| 181 return true; |
| 182 } |
| 183 |
| 184 void ProtectedPrefsWatcher::UpdateBackupSignature() { |
| 185 PrefService* prefs = profile_->GetPrefs(); |
| 186 std::string signed_data = GetSignatureData(prefs); |
| 187 DCHECK(!signed_data.empty()); |
| 188 std::string signature = SignSetting(signed_data); |
| 189 DCHECK(!signature.empty()); |
| 190 std::string signature_base64; |
| 191 if (!base::Base64Encode(signature, &signature_base64)) |
| 192 NOTREACHED(); |
| 193 prefs->SetString(kBackupSignature, signature_base64); |
| 194 // Schedule disk write on FILE thread as soon as possible. |
| 195 prefs->CommitPendingWrite(); |
| 196 VLOG(1) << "Updated backup signature"; |
| 197 } |
| 198 |
| 199 bool ProtectedPrefsWatcher::IsSignatureValid() const { |
| 200 DCHECK(HasBackup()); |
| 201 PrefService* prefs = profile_->GetPrefs(); |
| 202 std::string signed_data = GetSignatureData(prefs); |
| 203 DCHECK(!signed_data.empty()); |
| 204 std::string signature; |
| 205 if (!base::Base64Decode(prefs->GetString(kBackupSignature), &signature)) |
| 206 return false; |
| 207 return IsSettingValid(signed_data, signature); |
| 208 } |
| 209 |
| 210 void ProtectedPrefsWatcher::ValidateBackup() { |
| 211 if (!HasBackup()) { |
| 212 // Create initial backup entries and sign them. |
| 213 InitBackup(); |
| 214 UMA_HISTOGRAM_ENUMERATION( |
| 215 kProtectorHistogramPrefs, |
| 216 kProtectorErrorValueValidZero, |
| 217 kProtectorErrorCount); |
| 218 } else if (IsSignatureValid()) { |
| 219 UMA_HISTOGRAM_ENUMERATION( |
| 220 kProtectorHistogramPrefs, |
| 221 kProtectorErrorValueValid, |
| 222 kProtectorErrorCount); |
| 223 } else { |
| 224 LOG(WARNING) << "Invalid backup signature"; |
| 225 // Further changes to protected prefs will overwrite the signature. |
| 226 is_backup_valid_ = false; |
| 227 UMA_HISTOGRAM_ENUMERATION( |
| 228 kProtectorHistogramPrefs, |
| 229 kProtectorErrorBackupInvalid, |
| 230 kProtectorErrorCount); |
| 231 } |
| 232 } |
| 233 |
| 234 std::string ProtectedPrefsWatcher::GetSignatureData(PrefService* prefs) const { |
| 235 // std::stringstream data; |
| 236 std::string data = base::StringPrintf( |
| 237 "%s|%d|%d|%d", |
| 238 prefs->GetString(kBackupHomePage).c_str(), |
| 239 prefs->GetBoolean(kBackupHomePageIsNewTabPage) ? 1 : 0, |
| 240 prefs->GetBoolean(kBackupShowHomeButton) ? 1 : 0, |
| 241 prefs->GetInteger(kBackupRestoreOnStartup)); |
| 242 const base::ListValue* startup_urls = |
| 243 prefs->GetList(kBackupURLsToRestoreOnStartup); |
| 244 for (base::ListValue::const_iterator it = startup_urls->begin(); |
| 245 it != startup_urls->end(); ++it) { |
| 246 std::string url; |
| 247 if (!(*it)->GetAsString(&url)) |
| 248 NOTREACHED(); |
| 249 base::StringAppendF(&data, "|%s", url.c_str()); |
| 250 } |
| 251 // These are safe to use becase they are always up-to-date and returned in |
| 252 // a consistent (sorted) order. |
| 253 for (ExtensionPrefs::ExtensionIdSet::const_iterator it = |
| 254 cached_extension_ids_.begin(); |
| 255 it != cached_extension_ids_.end(); ++it) { |
| 256 base::StringAppendF(&data, "|%s", it->c_str()); |
| 257 } |
| 258 return data; |
| 259 } |
| 260 |
| 261 } // namespace protector |
OLD | NEW |