1// Copyright 2014 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 <string> 6 7#include "base/command_line.h" 8#include "base/files/file_path.h" 9#include "base/files/file_util.h" 10#include "base/json/json_file_value_serializer.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/metrics/histogram_base.h" 13#include "base/metrics/histogram_samples.h" 14#include "base/metrics/statistics_recorder.h" 15#include "base/path_service.h" 16#include "base/prefs/pref_service.h" 17#include "base/prefs/scoped_user_pref_update.h" 18#include "base/strings/string_number_conversions.h" 19#include "base/strings/string_util.h" 20#include "base/values.h" 21#include "build/build_config.h" 22#include "chrome/browser/extensions/extension_browsertest.h" 23#include "chrome/browser/extensions/extension_service.h" 24#include "chrome/browser/prefs/chrome_pref_service_factory.h" 25#include "chrome/browser/prefs/profile_pref_store_manager.h" 26#include "chrome/browser/prefs/session_startup_pref.h" 27#include "chrome/browser/profiles/profile.h" 28#include "chrome/browser/ui/browser.h" 29#include "chrome/common/chrome_constants.h" 30#include "chrome/common/chrome_paths.h" 31#include "chrome/common/pref_names.h" 32#include "chrome/test/base/testing_profile.h" 33#include "components/search_engines/default_search_manager.h" 34#include "content/public/common/content_switches.h" 35#include "extensions/browser/pref_names.h" 36#include "extensions/common/extension.h" 37 38#if defined(OS_CHROMEOS) 39#include "chromeos/chromeos_switches.h" 40#endif 41 42namespace { 43 44// Extension ID of chrome/test/data/extensions/good.crx 45const char kGoodCrxId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; 46 47// Explicit expectations from the caller of GetTrackedPrefHistogramCount(). This 48// enables detailed reporting of the culprit on failure. 49enum AllowedBuckets { 50 // Allow no samples in any buckets. 51 ALLOW_NONE = -1, 52 // Any integer between BEGIN_ALLOW_SINGLE_BUCKET and END_ALLOW_SINGLE_BUCKET 53 // indicates that only this specific bucket is allowed to have a sample. 54 BEGIN_ALLOW_SINGLE_BUCKET = 0, 55 END_ALLOW_SINGLE_BUCKET = 100, 56 // Allow any buckets (no extra verifications performed). 57 ALLOW_ANY 58}; 59 60// Returns the number of times |histogram_name| was reported so far; adding the 61// results of the first 100 buckets (there are only ~19 reporting IDs as of this 62// writing; varies depending on the platform). |allowed_buckets| hints at extra 63// requirements verified in this method (see AllowedBuckets for details). 64int GetTrackedPrefHistogramCount(const char* histogram_name, 65 int allowed_buckets) { 66 const base::HistogramBase* histogram = 67 base::StatisticsRecorder::FindHistogram(histogram_name); 68 if (!histogram) 69 return 0; 70 71 scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples()); 72 int sum = 0; 73 for (int i = 0; i < 100; ++i) { 74 int count_for_id = samples->GetCount(i); 75 EXPECT_GE(count_for_id, 0); 76 sum += count_for_id; 77 78 if (allowed_buckets == ALLOW_NONE || 79 (allowed_buckets != ALLOW_ANY && i != allowed_buckets)) { 80 EXPECT_EQ(0, count_for_id) << "Unexpected reporting_id: " << i; 81 } 82 } 83 return sum; 84} 85 86scoped_ptr<base::DictionaryValue> ReadPrefsDictionary( 87 const base::FilePath& pref_file) { 88 JSONFileValueSerializer serializer(pref_file); 89 int error_code = JSONFileValueSerializer::JSON_NO_ERROR; 90 std::string error_str; 91 scoped_ptr<base::Value> prefs( 92 serializer.Deserialize(&error_code, &error_str)); 93 if (!prefs || error_code != JSONFileValueSerializer::JSON_NO_ERROR) { 94 ADD_FAILURE() << "Error #" << error_code << ": " << error_str; 95 return scoped_ptr<base::DictionaryValue>(); 96 } 97 if (!prefs->IsType(base::Value::TYPE_DICTIONARY)) { 98 ADD_FAILURE(); 99 return scoped_ptr<base::DictionaryValue>(); 100 } 101 return scoped_ptr<base::DictionaryValue>( 102 static_cast<base::DictionaryValue*>(prefs.release())); 103} 104 105#define PREF_HASH_BROWSER_TEST(fixture, test_name) \ 106 IN_PROC_BROWSER_TEST_P(fixture, PRE_##test_name) { \ 107 SetupPreferences(); \ 108 } \ 109 IN_PROC_BROWSER_TEST_P(fixture, test_name) { \ 110 VerifyReactionToPrefAttack(); \ 111 } \ 112 INSTANTIATE_TEST_CASE_P( \ 113 fixture##Instance, \ 114 fixture, \ 115 testing::Values( \ 116 chrome_prefs::internals::kSettingsEnforcementGroupNoEnforcement, \ 117 chrome_prefs::internals::kSettingsEnforcementGroupEnforceAlways, \ 118 chrome_prefs::internals:: \ 119 kSettingsEnforcementGroupEnforceAlwaysWithDSE, \ 120 chrome_prefs::internals:: \ 121 kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE)); 122 123// A base fixture designed such that implementations do two things: 124// 1) Override all three pure-virtual methods below to setup, attack, and 125// verify preferenes throughout the tests provided by this fixture. 126// 2) Instantiate their test via the PREF_HASH_BROWSER_TEST macro above. 127// Based on top of ExtensionBrowserTest to allow easy interaction with the 128// ExtensionService. 129class PrefHashBrowserTestBase 130 : public ExtensionBrowserTest, 131 public testing::WithParamInterface<std::string> { 132 public: 133 // List of potential protection levels for this test in strict increasing 134 // order of protection levels. 135 enum SettingsProtectionLevel { 136 PROTECTION_DISABLED_ON_PLATFORM, 137 PROTECTION_DISABLED_FOR_GROUP, 138 PROTECTION_ENABLED_BASIC, 139 PROTECTION_ENABLED_DSE, 140 PROTECTION_ENABLED_EXTENSIONS, 141 // Represents the strongest level (i.e. always equivalent to the last one in 142 // terms of protection), leave this one last when adding new levels. 143 PROTECTION_ENABLED_ALL 144 }; 145 146 PrefHashBrowserTestBase() 147 : protection_level_(GetProtectionLevelFromTrialGroup(GetParam())) { 148 } 149 150 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 151 ExtensionBrowserTest::SetUpCommandLine(command_line); 152 EXPECT_FALSE(command_line->HasSwitch(switches::kForceFieldTrials)); 153 command_line->AppendSwitchASCII( 154 switches::kForceFieldTrials, 155 std::string(chrome_prefs::internals::kSettingsEnforcementTrialName) + 156 "/" + GetParam() + "/"); 157#if defined(OS_CHROMEOS) 158 command_line->AppendSwitch( 159 chromeos::switches::kIgnoreUserProfileMappingForTests); 160#endif 161 } 162 163 virtual bool SetUpUserDataDirectory() OVERRIDE { 164 // Do the normal setup in the PRE test and attack preferences in the main 165 // test. 166 if (IsPRETest()) 167 return ExtensionBrowserTest::SetUpUserDataDirectory(); 168 169#if defined(OS_CHROMEOS) 170 // For some reason, the Preferences file does not exist in the location 171 // below on Chrome OS. Since protection is disabled on Chrome OS, it's okay 172 // to simply not attack preferences at all (and still assert that no 173 // hardening related histogram kicked in in VerifyReactionToPrefAttack()). 174 // TODO(gab): Figure out why there is no Preferences file in this location 175 // on Chrome OS (and re-enable the section disabled for OS_CHROMEOS further 176 // below). 177 EXPECT_EQ(PROTECTION_DISABLED_ON_PLATFORM, protection_level_); 178 return true; 179#endif 180 181 base::FilePath profile_dir; 182 EXPECT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &profile_dir)); 183 profile_dir = profile_dir.AppendASCII(TestingProfile::kTestUserProfileDir); 184 185 // Sanity check that old protected pref file is never present in modern 186 // Chromes. 187 EXPECT_FALSE(base::PathExists( 188 profile_dir.Append(chrome::kProtectedPreferencesFilenameDeprecated))); 189 190 // Read the preferences from disk. 191 192 const base::FilePath unprotected_pref_file = 193 profile_dir.Append(chrome::kPreferencesFilename); 194 EXPECT_TRUE(base::PathExists(unprotected_pref_file)); 195 196 const base::FilePath protected_pref_file = 197 profile_dir.Append(chrome::kSecurePreferencesFilename); 198 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM, 199 base::PathExists(protected_pref_file)); 200 201 scoped_ptr<base::DictionaryValue> unprotected_preferences( 202 ReadPrefsDictionary(unprotected_pref_file)); 203 if (!unprotected_preferences) 204 return false; 205 206 scoped_ptr<base::DictionaryValue> protected_preferences; 207 if (protection_level_ > PROTECTION_DISABLED_ON_PLATFORM) { 208 protected_preferences = ReadPrefsDictionary(protected_pref_file); 209 if (!protected_preferences) 210 return false; 211 } 212 213 // Let the underlying test modify the preferences. 214 AttackPreferencesOnDisk(unprotected_preferences.get(), 215 protected_preferences.get()); 216 217 // Write the modified preferences back to disk. 218 219 JSONFileValueSerializer unprotected_prefs_serializer(unprotected_pref_file); 220 EXPECT_TRUE( 221 unprotected_prefs_serializer.Serialize(*unprotected_preferences)); 222 223 if (protected_preferences) { 224 JSONFileValueSerializer protected_prefs_serializer(protected_pref_file); 225 EXPECT_TRUE(protected_prefs_serializer.Serialize(*protected_preferences)); 226 } 227 228 return true; 229 } 230 231 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 232 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture(); 233 234 // Bots are on a domain, turn off the domain check for settings hardening in 235 // order to be able to test all SettingsEnforcement groups. 236 chrome_prefs::DisableDelaysAndDomainCheckForTesting(); 237 } 238 239 // In the PRE_ test, find the number of tracked preferences that were 240 // initialized and save it to a file to be read back in the main test and used 241 // as the total number of tracked preferences. 242 virtual void SetUpOnMainThread() OVERRIDE { 243 ExtensionBrowserTest::SetUpOnMainThread(); 244 245 // File in which the PRE_ test will save the number of tracked preferences 246 // on this platform. 247 const char kNumTrackedPrefFilename[] = "NumTrackedPrefs"; 248 249 base::FilePath num_tracked_prefs_file; 250 ASSERT_TRUE( 251 PathService::Get(chrome::DIR_USER_DATA, &num_tracked_prefs_file)); 252 num_tracked_prefs_file = 253 num_tracked_prefs_file.AppendASCII(kNumTrackedPrefFilename); 254 255 if (IsPRETest()) { 256 num_tracked_prefs_ = GetTrackedPrefHistogramCount( 257 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_ANY); 258 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM, 259 num_tracked_prefs_ > 0); 260 261 // Split tracked prefs are reported as Unchanged not as TrustedInitialized 262 // when an empty dictionary is encountered on first run (this should only 263 // hit for pref #5 in the current design). 264 int num_split_tracked_prefs = GetTrackedPrefHistogramCount( 265 "Settings.TrackedPreferenceUnchanged", BEGIN_ALLOW_SINGLE_BUCKET + 5); 266 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM ? 1 : 0, 267 num_split_tracked_prefs); 268 269 num_tracked_prefs_ += num_split_tracked_prefs; 270 271 std::string num_tracked_prefs_str = base::IntToString(num_tracked_prefs_); 272 EXPECT_EQ(static_cast<int>(num_tracked_prefs_str.size()), 273 base::WriteFile(num_tracked_prefs_file, 274 num_tracked_prefs_str.c_str(), 275 num_tracked_prefs_str.size())); 276 } else { 277 std::string num_tracked_prefs_str; 278 EXPECT_TRUE(base::ReadFileToString(num_tracked_prefs_file, 279 &num_tracked_prefs_str)); 280 EXPECT_TRUE( 281 base::StringToInt(num_tracked_prefs_str, &num_tracked_prefs_)); 282 } 283 } 284 285 protected: 286 // Called from the PRE_ test's body. Overrides should use it to setup 287 // preferences through Chrome. 288 virtual void SetupPreferences() = 0; 289 290 // Called prior to the main test launching its browser. Overrides should use 291 // it to attack preferences. |(un)protected_preferences| represent the state 292 // on disk prior to launching the main test, they can be modified by this 293 // method and modifications will be flushed back to disk before launching the 294 // main test. |unprotected_preferences| is never NULL, |protected_preferences| 295 // may be NULL if in PROTECTION_DISABLED_ON_PLATFORM mode. 296 virtual void AttackPreferencesOnDisk( 297 base::DictionaryValue* unprotected_preferences, 298 base::DictionaryValue* protected_preferences) = 0; 299 300 // Called from the body of the main test. Overrides should use it to verify 301 // that the browser had the desired reaction when faced when the attack 302 // orchestrated in AttackPreferencesOnDisk(). 303 virtual void VerifyReactionToPrefAttack() = 0; 304 305 int num_tracked_prefs() const { return num_tracked_prefs_; } 306 307 const SettingsProtectionLevel protection_level_; 308 309 private: 310 // Returns true if this is the PRE_ phase of the test. 311 bool IsPRETest() { 312 return StartsWithASCII( 313 testing::UnitTest::GetInstance()->current_test_info()->name(), 314 "PRE_", 315 true /* case_sensitive */); 316 } 317 318 SettingsProtectionLevel GetProtectionLevelFromTrialGroup( 319 const std::string& trial_group) { 320 if (!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking) 321 return PROTECTION_DISABLED_ON_PLATFORM; 322 323// Protection levels can't be adjusted via --force-fieldtrials in official 324// builds. 325#if defined(OFFICIAL_BUILD) 326 327#if defined(OS_WIN) 328 // The strongest mode is enforced on Windows in the absence of a field 329 // trial. 330 return PROTECTION_ENABLED_ALL; 331#else 332 return PROTECTION_DISABLED_FOR_GROUP; 333#endif 334 335#else // defined(OFFICIAL_BUILD) 336 337 using namespace chrome_prefs::internals; 338 if (trial_group == kSettingsEnforcementGroupNoEnforcement) { 339 return PROTECTION_DISABLED_FOR_GROUP; 340 } else if (trial_group == kSettingsEnforcementGroupEnforceAlways) { 341 return PROTECTION_ENABLED_BASIC; 342 } else if (trial_group == kSettingsEnforcementGroupEnforceAlwaysWithDSE) { 343 return PROTECTION_ENABLED_DSE; 344 } else if (trial_group == 345 kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE) { 346 return PROTECTION_ENABLED_EXTENSIONS; 347 } else { 348 ADD_FAILURE(); 349 return static_cast<SettingsProtectionLevel>(-1); 350 } 351 352#endif // defined(OFFICIAL_BUILD) 353 354 } 355 356 int num_tracked_prefs_; 357}; 358 359} // namespace 360 361// Verifies that nothing is reset when nothing is tampered with. 362// Also sanity checks that the expected preferences files are in place. 363class PrefHashBrowserTestUnchangedDefault : public PrefHashBrowserTestBase { 364 public: 365 virtual void SetupPreferences() OVERRIDE { 366 // Default Chrome setup. 367 } 368 369 virtual void AttackPreferencesOnDisk( 370 base::DictionaryValue* unprotected_preferences, 371 base::DictionaryValue* protected_preferences) OVERRIDE { 372 // No attack. 373 } 374 375 virtual void VerifyReactionToPrefAttack() OVERRIDE { 376 // Expect all prefs to be reported as Unchanged with no resets. 377 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM 378 ? num_tracked_prefs() : 0, 379 GetTrackedPrefHistogramCount( 380 "Settings.TrackedPreferenceUnchanged", ALLOW_ANY)); 381 EXPECT_EQ(0, 382 GetTrackedPrefHistogramCount( 383 "Settings.TrackedPreferenceWantedReset", ALLOW_NONE)); 384 EXPECT_EQ(0, 385 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 386 ALLOW_NONE)); 387 388 // Nothing else should have triggered. 389 EXPECT_EQ(0, 390 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 391 ALLOW_NONE)); 392 EXPECT_EQ(0, 393 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 394 ALLOW_NONE)); 395 EXPECT_EQ(0, 396 GetTrackedPrefHistogramCount( 397 "Settings.TrackedPreferenceInitialized", ALLOW_NONE)); 398 EXPECT_EQ(0, 399 GetTrackedPrefHistogramCount( 400 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_NONE)); 401 EXPECT_EQ( 402 0, 403 GetTrackedPrefHistogramCount( 404 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 405 } 406}; 407 408PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUnchangedDefault, UnchangedDefault); 409 410// Augments PrefHashBrowserTestUnchangedDefault to confirm that nothing is reset 411// when nothing is tampered with, even if Chrome itself wrote custom prefs in 412// its last run. 413class PrefHashBrowserTestUnchangedCustom 414 : public PrefHashBrowserTestUnchangedDefault { 415 public: 416 virtual void SetupPreferences() OVERRIDE { 417 profile()->GetPrefs()->SetString(prefs::kHomePage, "http://example.com"); 418 419 InstallExtensionWithUIAutoConfirm( 420 test_data_dir_.AppendASCII("good.crx"), 1, browser()); 421 } 422 423 virtual void VerifyReactionToPrefAttack() OVERRIDE { 424 // Make sure the settings written in the last run stuck. 425 EXPECT_EQ("http://example.com", 426 profile()->GetPrefs()->GetString(prefs::kHomePage)); 427 428 EXPECT_TRUE(extension_service()->GetExtensionById(kGoodCrxId, false)); 429 430 // Reaction should be identical to unattacked default prefs. 431 PrefHashBrowserTestUnchangedDefault::VerifyReactionToPrefAttack(); 432 } 433}; 434 435PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUnchangedCustom, UnchangedCustom); 436 437// Verifies that cleared prefs are reported. 438class PrefHashBrowserTestClearedAtomic : public PrefHashBrowserTestBase { 439 public: 440 virtual void SetupPreferences() OVERRIDE { 441 profile()->GetPrefs()->SetString(prefs::kHomePage, "http://example.com"); 442 } 443 444 virtual void AttackPreferencesOnDisk( 445 base::DictionaryValue* unprotected_preferences, 446 base::DictionaryValue* protected_preferences) OVERRIDE { 447 base::DictionaryValue* selected_prefs = 448 protection_level_ >= PROTECTION_ENABLED_BASIC ? protected_preferences 449 : unprotected_preferences; 450 // |selected_prefs| should never be NULL under the protection level picking 451 // it. 452 EXPECT_TRUE(selected_prefs); 453 EXPECT_TRUE(selected_prefs->Remove(prefs::kHomePage, NULL)); 454 } 455 456 virtual void VerifyReactionToPrefAttack() OVERRIDE { 457 // The clearance of homepage should have been noticed (as pref #2 being 458 // cleared), but shouldn't have triggered a reset (as there is nothing we 459 // can do when the pref is already gone). 460 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM ? 1 : 0, 461 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 462 BEGIN_ALLOW_SINGLE_BUCKET + 2)); 463 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM 464 ? num_tracked_prefs() - 1 : 0, 465 GetTrackedPrefHistogramCount( 466 "Settings.TrackedPreferenceUnchanged", ALLOW_ANY)); 467 EXPECT_EQ(0, 468 GetTrackedPrefHistogramCount( 469 "Settings.TrackedPreferenceWantedReset", ALLOW_NONE)); 470 EXPECT_EQ(0, 471 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 472 ALLOW_NONE)); 473 474 // Nothing else should have triggered. 475 EXPECT_EQ(0, 476 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 477 ALLOW_NONE)); 478 EXPECT_EQ(0, 479 GetTrackedPrefHistogramCount( 480 "Settings.TrackedPreferenceInitialized", ALLOW_NONE)); 481 EXPECT_EQ(0, 482 GetTrackedPrefHistogramCount( 483 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_NONE)); 484 EXPECT_EQ( 485 0, 486 GetTrackedPrefHistogramCount( 487 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 488 } 489}; 490 491PREF_HASH_BROWSER_TEST(PrefHashBrowserTestClearedAtomic, ClearedAtomic); 492 493// Verifies that clearing the MACs results in untrusted Initialized pings for 494// non-null protected prefs. 495class PrefHashBrowserTestUntrustedInitialized : public PrefHashBrowserTestBase { 496 public: 497 virtual void SetupPreferences() OVERRIDE { 498 // Explicitly set the DSE (it's otherwise NULL by default, preventing 499 // thorough testing of the PROTECTION_ENABLED_DSE level). 500 DefaultSearchManager default_search_manager( 501 profile()->GetPrefs(), DefaultSearchManager::ObserverCallback()); 502 DefaultSearchManager::Source dse_source = 503 static_cast<DefaultSearchManager::Source>(-1); 504 505 const TemplateURLData* default_template_url_data = 506 default_search_manager.GetDefaultSearchEngine(&dse_source); 507 EXPECT_EQ(DefaultSearchManager::FROM_FALLBACK, dse_source); 508 509 default_search_manager.SetUserSelectedDefaultSearchEngine( 510 *default_template_url_data); 511 512 default_search_manager.GetDefaultSearchEngine(&dse_source); 513 EXPECT_EQ(DefaultSearchManager::FROM_USER, dse_source); 514 515 // Also explicitly set an atomic pref that falls under 516 // PROTECTION_ENABLED_BASIC. 517 profile()->GetPrefs()->SetInteger(prefs::kRestoreOnStartup, 518 SessionStartupPref::URLS); 519 } 520 521 virtual void AttackPreferencesOnDisk( 522 base::DictionaryValue* unprotected_preferences, 523 base::DictionaryValue* protected_preferences) OVERRIDE { 524 EXPECT_TRUE(unprotected_preferences->Remove("protection.macs", NULL)); 525 if (protected_preferences) 526 EXPECT_TRUE(protected_preferences->Remove("protection.macs", NULL)); 527 } 528 529 virtual void VerifyReactionToPrefAttack() OVERRIDE { 530 // Preferences that are NULL by default will be TrustedInitialized. 531 int num_null_values = GetTrackedPrefHistogramCount( 532 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_ANY); 533 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM, 534 num_null_values > 0); 535 if (num_null_values > 0) { 536 // This test requires that at least 3 prefs be non-null (extensions, DSE, 537 // and 1 atomic pref explictly set for this test above). 538 EXPECT_LT(num_null_values, num_tracked_prefs() - 3); 539 } 540 541 // Expect all non-null prefs to be reported as Initialized (with 542 // accompanying resets or wanted resets based on the current protection 543 // level). 544 EXPECT_EQ(num_tracked_prefs() - num_null_values, 545 GetTrackedPrefHistogramCount( 546 "Settings.TrackedPreferenceInitialized", ALLOW_ANY)); 547 548 int num_protected_prefs = 0; 549 // A switch statement falling through each protection level in decreasing 550 // levels of protection to add expectations for each level which augments 551 // the previous one. 552 switch (protection_level_) { 553 case PROTECTION_ENABLED_ALL: 554 // Falls through. 555 case PROTECTION_ENABLED_EXTENSIONS: 556 ++num_protected_prefs; 557 // Falls through. 558 case PROTECTION_ENABLED_DSE: 559 ++num_protected_prefs; 560 // Falls through. 561 case PROTECTION_ENABLED_BASIC: 562 num_protected_prefs += num_tracked_prefs() - num_null_values - 2; 563 // Falls through. 564 case PROTECTION_DISABLED_FOR_GROUP: 565 // No protection. Falls through. 566 case PROTECTION_DISABLED_ON_PLATFORM: 567 // No protection. 568 break; 569 } 570 571 EXPECT_EQ(num_tracked_prefs() - num_null_values - num_protected_prefs, 572 GetTrackedPrefHistogramCount( 573 "Settings.TrackedPreferenceWantedReset", ALLOW_ANY)); 574 EXPECT_EQ(num_protected_prefs, 575 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 576 ALLOW_ANY)); 577 578 // Explicitly verify the result of reported resets. 579 580 DefaultSearchManager default_search_manager( 581 profile()->GetPrefs(), DefaultSearchManager::ObserverCallback()); 582 DefaultSearchManager::Source dse_source = 583 static_cast<DefaultSearchManager::Source>(-1); 584 default_search_manager.GetDefaultSearchEngine(&dse_source); 585 EXPECT_EQ(protection_level_ < PROTECTION_ENABLED_DSE 586 ? DefaultSearchManager::FROM_USER 587 : DefaultSearchManager::FROM_FALLBACK, 588 dse_source); 589 590 EXPECT_EQ(protection_level_ < PROTECTION_ENABLED_BASIC, 591 profile()->GetPrefs()->GetInteger(prefs::kRestoreOnStartup) == 592 SessionStartupPref::URLS); 593 594 // Nothing else should have triggered. 595 EXPECT_EQ(0, 596 GetTrackedPrefHistogramCount( 597 "Settings.TrackedPreferenceUnchanged", ALLOW_NONE)); 598 EXPECT_EQ(0, 599 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 600 ALLOW_NONE)); 601 EXPECT_EQ(0, 602 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 603 ALLOW_NONE)); 604 EXPECT_EQ( 605 0, 606 GetTrackedPrefHistogramCount( 607 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 608 } 609}; 610 611PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUntrustedInitialized, 612 UntrustedInitialized); 613 614// Verifies that changing an atomic pref results in it being reported (and reset 615// if the protection level allows it). 616class PrefHashBrowserTestChangedAtomic : public PrefHashBrowserTestBase { 617 public: 618 virtual void SetupPreferences() OVERRIDE { 619 profile()->GetPrefs()->SetInteger(prefs::kRestoreOnStartup, 620 SessionStartupPref::URLS); 621 622 ListPrefUpdate update(profile()->GetPrefs(), 623 prefs::kURLsToRestoreOnStartup); 624 update->AppendString("http://example.com"); 625 } 626 627 virtual void AttackPreferencesOnDisk( 628 base::DictionaryValue* unprotected_preferences, 629 base::DictionaryValue* protected_preferences) OVERRIDE { 630 base::DictionaryValue* selected_prefs = 631 protection_level_ >= PROTECTION_ENABLED_BASIC ? protected_preferences 632 : unprotected_preferences; 633 // |selected_prefs| should never be NULL under the protection level picking 634 // it. 635 EXPECT_TRUE(selected_prefs); 636 base::ListValue* startup_urls; 637 EXPECT_TRUE( 638 selected_prefs->GetList(prefs::kURLsToRestoreOnStartup, &startup_urls)); 639 EXPECT_TRUE(startup_urls); 640 EXPECT_EQ(1U, startup_urls->GetSize()); 641 startup_urls->AppendString("http://example.org"); 642 } 643 644 virtual void VerifyReactionToPrefAttack() OVERRIDE { 645 // Expect a single Changed event for tracked pref #4 (startup URLs). 646 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM ? 1 : 0, 647 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 648 BEGIN_ALLOW_SINGLE_BUCKET + 4)); 649 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM 650 ? num_tracked_prefs() - 1 : 0, 651 GetTrackedPrefHistogramCount( 652 "Settings.TrackedPreferenceUnchanged", ALLOW_ANY)); 653 654 EXPECT_EQ( 655 (protection_level_ > PROTECTION_DISABLED_ON_PLATFORM && 656 protection_level_ < PROTECTION_ENABLED_BASIC) ? 1 : 0, 657 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceWantedReset", 658 BEGIN_ALLOW_SINGLE_BUCKET + 4)); 659 EXPECT_EQ(protection_level_ >= PROTECTION_ENABLED_BASIC ? 1 : 0, 660 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 661 BEGIN_ALLOW_SINGLE_BUCKET + 4)); 662 663// TODO(gab): This doesn't work on OS_CHROMEOS because we fail to attack 664// Preferences. 665#if !defined(OS_CHROMEOS) 666 // Explicitly verify the result of reported resets. 667 EXPECT_EQ(protection_level_ >= PROTECTION_ENABLED_BASIC ? 0U : 2U, 668 profile() 669 ->GetPrefs() 670 ->GetList(prefs::kURLsToRestoreOnStartup) 671 ->GetSize()); 672#endif 673 674 // Nothing else should have triggered. 675 EXPECT_EQ(0, 676 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 677 ALLOW_NONE)); 678 EXPECT_EQ(0, 679 GetTrackedPrefHistogramCount( 680 "Settings.TrackedPreferenceInitialized", ALLOW_NONE)); 681 EXPECT_EQ(0, 682 GetTrackedPrefHistogramCount( 683 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_NONE)); 684 EXPECT_EQ( 685 0, 686 GetTrackedPrefHistogramCount( 687 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 688 } 689}; 690 691PREF_HASH_BROWSER_TEST(PrefHashBrowserTestChangedAtomic, ChangedAtomic); 692 693// Verifies that changing or adding an entry in a split pref results in both 694// items being reported (and remove if the protection level allows it). 695class PrefHashBrowserTestChangedSplitPref : public PrefHashBrowserTestBase { 696 public: 697 virtual void SetupPreferences() OVERRIDE { 698 InstallExtensionWithUIAutoConfirm( 699 test_data_dir_.AppendASCII("good.crx"), 1, browser()); 700 } 701 702 virtual void AttackPreferencesOnDisk( 703 base::DictionaryValue* unprotected_preferences, 704 base::DictionaryValue* protected_preferences) OVERRIDE { 705 base::DictionaryValue* selected_prefs = 706 protection_level_ >= PROTECTION_ENABLED_EXTENSIONS 707 ? protected_preferences 708 : unprotected_preferences; 709 // |selected_prefs| should never be NULL under the protection level picking 710 // it. 711 EXPECT_TRUE(selected_prefs); 712 base::DictionaryValue* extensions_dict; 713 EXPECT_TRUE(selected_prefs->GetDictionary( 714 extensions::pref_names::kExtensions, &extensions_dict)); 715 EXPECT_TRUE(extensions_dict); 716 717 // Tamper with any installed setting for good.crx 718 base::DictionaryValue* good_crx_dict; 719 EXPECT_TRUE(extensions_dict->GetDictionary(kGoodCrxId, &good_crx_dict)); 720 int good_crx_state; 721 EXPECT_TRUE(good_crx_dict->GetInteger("state", &good_crx_state)); 722 EXPECT_EQ(extensions::Extension::ENABLED, good_crx_state); 723 good_crx_dict->SetInteger("state", extensions::Extension::DISABLED); 724 725 // Drop a fake extension (for the purpose of this test, dropped settings 726 // don't need to be valid extension settings). 727 base::DictionaryValue* fake_extension = new base::DictionaryValue; 728 fake_extension->SetString("name", "foo"); 729 extensions_dict->Set(std::string(32, 'a'), fake_extension); 730 } 731 732 virtual void VerifyReactionToPrefAttack() OVERRIDE { 733 // Expect a single split pref changed report with a count of 2 for tracked 734 // pref #5 (extensions). 735 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM ? 1 : 0, 736 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 737 BEGIN_ALLOW_SINGLE_BUCKET + 5)); 738 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM ? 1 : 0, 739 GetTrackedPrefHistogramCount( 740 "Settings.TrackedSplitPreferenceChanged.extensions.settings", 741 BEGIN_ALLOW_SINGLE_BUCKET + 2)); 742 743 // Everything else should have remained unchanged. 744 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM 745 ? num_tracked_prefs() - 1 : 0, 746 GetTrackedPrefHistogramCount( 747 "Settings.TrackedPreferenceUnchanged", ALLOW_ANY)); 748 749 EXPECT_EQ( 750 (protection_level_ > PROTECTION_DISABLED_ON_PLATFORM && 751 protection_level_ < PROTECTION_ENABLED_EXTENSIONS) ? 1 : 0, 752 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceWantedReset", 753 BEGIN_ALLOW_SINGLE_BUCKET + 5)); 754 EXPECT_EQ(protection_level_ >= PROTECTION_ENABLED_EXTENSIONS ? 1 : 0, 755 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 756 BEGIN_ALLOW_SINGLE_BUCKET + 5)); 757 758 EXPECT_EQ(protection_level_ < PROTECTION_ENABLED_EXTENSIONS, 759 extension_service()->GetExtensionById(kGoodCrxId, true) != NULL); 760 761 // Nothing else should have triggered. 762 EXPECT_EQ(0, 763 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 764 ALLOW_NONE)); 765 EXPECT_EQ(0, 766 GetTrackedPrefHistogramCount( 767 "Settings.TrackedPreferenceInitialized", ALLOW_NONE)); 768 EXPECT_EQ(0, 769 GetTrackedPrefHistogramCount( 770 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_NONE)); 771 EXPECT_EQ( 772 0, 773 GetTrackedPrefHistogramCount( 774 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 775 } 776}; 777 778PREF_HASH_BROWSER_TEST(PrefHashBrowserTestChangedSplitPref, ChangedSplitPref); 779 780// Verifies that adding a value to unprotected preferences for a key which is 781// still using the default (i.e. has no value stored in protected preferences) 782// doesn't allow that value to slip in with no valid MAC (regression test for 783// http://crbug.com/414554) 784class PrefHashBrowserTestUntrustedAdditionToPrefs 785 : public PrefHashBrowserTestBase { 786 public: 787 virtual void SetupPreferences() OVERRIDE { 788 // Ensure there is no user-selected value for kRestoreOnStartup. 789 EXPECT_FALSE( 790 profile()->GetPrefs()->GetUserPrefValue(prefs::kRestoreOnStartup)); 791 } 792 793 virtual void AttackPreferencesOnDisk( 794 base::DictionaryValue* unprotected_preferences, 795 base::DictionaryValue* protected_preferences) OVERRIDE { 796 unprotected_preferences->SetInteger(prefs::kRestoreOnStartup, 797 SessionStartupPref::LAST); 798 } 799 800 virtual void VerifyReactionToPrefAttack() OVERRIDE { 801 // Expect a single Changed event for tracked pref #3 (kRestoreOnStartup) if 802 // not protecting; if protection is enabled the change should be a no-op. 803 int changed_expected = 804 protection_level_ == PROTECTION_DISABLED_FOR_GROUP ? 1 : 0; 805 EXPECT_EQ( 806 (protection_level_ > PROTECTION_DISABLED_ON_PLATFORM && 807 protection_level_ < PROTECTION_ENABLED_BASIC) ? changed_expected : 0, 808 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 809 BEGIN_ALLOW_SINGLE_BUCKET + 3)); 810 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM 811 ? num_tracked_prefs() - changed_expected : 0, 812 GetTrackedPrefHistogramCount( 813 "Settings.TrackedPreferenceUnchanged", ALLOW_ANY)); 814 815 EXPECT_EQ( 816 (protection_level_ > PROTECTION_DISABLED_ON_PLATFORM && 817 protection_level_ < PROTECTION_ENABLED_BASIC) ? 1 : 0, 818 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceWantedReset", 819 BEGIN_ALLOW_SINGLE_BUCKET + 3)); 820 EXPECT_EQ(0, 821 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 822 ALLOW_NONE)); 823 824 // Nothing else should have triggered. 825 EXPECT_EQ(0, 826 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 827 ALLOW_NONE)); 828 EXPECT_EQ(0, 829 GetTrackedPrefHistogramCount( 830 "Settings.TrackedPreferenceInitialized", ALLOW_NONE)); 831 EXPECT_EQ(0, 832 GetTrackedPrefHistogramCount( 833 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_NONE)); 834 EXPECT_EQ( 835 0, 836 GetTrackedPrefHistogramCount( 837 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 838 } 839}; 840 841PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUntrustedAdditionToPrefs, 842 UntrustedAdditionToPrefs); 843 844// Verifies that adding a value to unprotected preferences while wiping a 845// user-selected value from protected preferences doesn't allow that value to 846// slip in with no valid MAC (regression test for http://crbug.com/414554). 847class PrefHashBrowserTestUntrustedAdditionToPrefsAfterWipe 848 : public PrefHashBrowserTestBase { 849 public: 850 virtual void SetupPreferences() OVERRIDE { 851 profile()->GetPrefs()->SetString(prefs::kHomePage, "http://example.com"); 852 } 853 854 virtual void AttackPreferencesOnDisk( 855 base::DictionaryValue* unprotected_preferences, 856 base::DictionaryValue* protected_preferences) OVERRIDE { 857 // Set or change the value in Preferences to the attacker's choice. 858 unprotected_preferences->SetString(prefs::kHomePage, "http://example.net"); 859 // Clear the value in Secure Preferences, if any. 860 if (protected_preferences) 861 protected_preferences->Remove(prefs::kHomePage, NULL); 862 } 863 864 virtual void VerifyReactionToPrefAttack() OVERRIDE { 865 // Expect a single Changed event for tracked pref #2 (kHomePage) if 866 // not protecting; if protection is enabled the change should be a Cleared. 867 int changed_expected = 868 protection_level_ > PROTECTION_DISABLED_ON_PLATFORM && 869 protection_level_ < PROTECTION_ENABLED_BASIC 870 ? 1 : 0; 871 int cleared_expected = 872 protection_level_ >= PROTECTION_ENABLED_BASIC 873 ? 1 : 0; 874 EXPECT_EQ(changed_expected, 875 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged", 876 BEGIN_ALLOW_SINGLE_BUCKET + 2)); 877 EXPECT_EQ(cleared_expected, 878 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared", 879 BEGIN_ALLOW_SINGLE_BUCKET + 2)); 880 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM 881 ? num_tracked_prefs() - changed_expected - cleared_expected 882 : 0, 883 GetTrackedPrefHistogramCount( 884 "Settings.TrackedPreferenceUnchanged", ALLOW_ANY)); 885 886 EXPECT_EQ( 887 changed_expected, 888 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceWantedReset", 889 BEGIN_ALLOW_SINGLE_BUCKET + 2)); 890 EXPECT_EQ(0, 891 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", 892 ALLOW_NONE)); 893 894 // Nothing else should have triggered. 895 EXPECT_EQ(0, 896 GetTrackedPrefHistogramCount( 897 "Settings.TrackedPreferenceInitialized", ALLOW_NONE)); 898 EXPECT_EQ(0, 899 GetTrackedPrefHistogramCount( 900 "Settings.TrackedPreferenceTrustedInitialized", ALLOW_NONE)); 901 EXPECT_EQ( 902 0, 903 GetTrackedPrefHistogramCount( 904 "Settings.TrackedPreferenceMigratedLegacyDeviceId", ALLOW_NONE)); 905 } 906}; 907 908PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUntrustedAdditionToPrefsAfterWipe, 909 UntrustedAdditionToPrefsAfterWipe); 910