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