Home | History | Annotate | Download | only in browser
      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 "base/bind.h"
      6 #include "base/metrics/field_trial.h"
      7 #include "base/metrics/histogram.h"
      8 #include "base/metrics/sparse_histogram.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/win/registry.h"
     11 #include "chrome/browser/chrome_elf_init_win.h"
     12 #include "chrome_elf/blacklist/blacklist.h"
     13 #include "chrome_elf/chrome_elf_constants.h"
     14 #include "chrome_elf/dll_hash/dll_hash.h"
     15 #include "components/variations/variations_associated_data.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "version.h"  // NOLINT
     18 
     19 const char kBrowserBlacklistTrialName[] = "BrowserBlacklist";
     20 const char kBrowserBlacklistTrialDisabledGroupName[] = "NoBlacklist";
     21 
     22 namespace {
     23 
     24 // How long to wait, in seconds, before reporting for the second (and last
     25 // time), what dlls were blocked from the browser process.
     26 const int kBlacklistReportingDelaySec = 600;
     27 
     28 // This enum is used to define the buckets for an enumerated UMA histogram.
     29 // Hence,
     30 //   (a) existing enumerated constants should never be deleted or reordered, and
     31 //   (b) new constants should only be appended in front of
     32 //       BLACKLIST_SETUP_EVENT_MAX.
     33 enum BlacklistSetupEventType {
     34   // The blacklist beacon has placed to enable the browser blacklisting.
     35   BLACKLIST_SETUP_ENABLED = 0,
     36 
     37   // The blacklist was successfully enabled.
     38   BLACKLIST_SETUP_RAN_SUCCESSFULLY,
     39 
     40   // The blacklist setup code failed to execute.
     41   BLACKLIST_SETUP_FAILED,
     42 
     43   // The blacklist thunk setup code failed. This is probably an indication
     44   // that something else patched that code first.
     45   BLACKLIST_THUNK_SETUP_FAILED,
     46 
     47   // Deprecated. The blacklist interception code failed to execute.
     48   BLACKLIST_INTERCEPTION_FAILED,
     49 
     50   // The blacklist was disabled for this run (after it failed too many times).
     51   BLACKLIST_SETUP_DISABLED,
     52 
     53   // Always keep this at the end.
     54   BLACKLIST_SETUP_EVENT_MAX,
     55 };
     56 
     57 void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) {
     58   UMA_HISTOGRAM_ENUMERATION("Blacklist.Setup",
     59                             blacklist_setup_event,
     60                             BLACKLIST_SETUP_EVENT_MAX);
     61 }
     62 
     63 // Report which DLLs were prevented from being loaded.
     64 void ReportSuccessfulBlocks() {
     65   // Figure out how many dlls were blocked.
     66   int num_blocked_dlls = 0;
     67   blacklist::SuccessfullyBlocked(NULL, &num_blocked_dlls);
     68 
     69   if (num_blocked_dlls == 0)
     70     return;
     71 
     72   // Now retrieve the list of blocked dlls.
     73   std::vector<const wchar_t*> blocked_dlls(num_blocked_dlls);
     74   blacklist::SuccessfullyBlocked(&blocked_dlls[0], &num_blocked_dlls);
     75 
     76   // Send up the hashes of the blocked dlls via UMA.
     77   for (size_t i = 0; i < blocked_dlls.size(); ++i) {
     78     std::string dll_name_utf8;
     79     base::WideToUTF8(blocked_dlls[i], wcslen(blocked_dlls[i]), &dll_name_utf8);
     80     int uma_hash = DllNameToHash(dll_name_utf8);
     81 
     82     UMA_HISTOGRAM_SPARSE_SLOWLY("Blacklist.Blocked", uma_hash);
     83   }
     84 }
     85 
     86 }  // namespace
     87 
     88 void InitializeChromeElf() {
     89   if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) ==
     90       kBrowserBlacklistTrialDisabledGroupName) {
     91     // Disable the blacklist for all future runs by removing the beacon.
     92     base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER);
     93     blacklist_registry_key.DeleteKey(blacklist::kRegistryBeaconPath);
     94   } else {
     95     AddFinchBlacklistToRegistry();
     96     BrowserBlacklistBeaconSetup();
     97   }
     98 
     99   // Report all successful blacklist interceptions.
    100   ReportSuccessfulBlocks();
    101 
    102   // Schedule another task to report all sucessful interceptions later.
    103   // This time delay should be long enough to catch any dlls that attempt to
    104   // inject after Chrome has started up.
    105   content::BrowserThread::PostDelayedTask(
    106       content::BrowserThread::UI,
    107       FROM_HERE,
    108       base::Bind(&ReportSuccessfulBlocks),
    109       base::TimeDelta::FromSeconds(kBlacklistReportingDelaySec));
    110 }
    111 
    112 // Note that running multiple chrome instances with distinct user data
    113 // directories could lead to deletion (and/or replacement) of the finch
    114 // blacklist registry data in one instance before the second has a chance to
    115 // read those values.
    116 void AddFinchBlacklistToRegistry() {
    117   base::win::RegKey finch_blacklist_registry_key(
    118       HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE);
    119 
    120   // No point in trying to continue if the registry key isn't valid.
    121   if (!finch_blacklist_registry_key.Valid())
    122     return;
    123 
    124   // Delete and recreate the key to clear the registry.
    125   finch_blacklist_registry_key.DeleteKey(L"");
    126   finch_blacklist_registry_key.Create(
    127       HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE);
    128 
    129   std::map<std::string, std::string> params;
    130   variations::GetVariationParams(kBrowserBlacklistTrialName, &params);
    131 
    132   for (std::map<std::string, std::string>::iterator it = params.begin();
    133        it != params.end();
    134        ++it) {
    135     std::wstring name = base::UTF8ToWide(it->first);
    136     std::wstring val = base::UTF8ToWide(it->second);
    137 
    138     finch_blacklist_registry_key.WriteValue(name.c_str(), val.c_str());
    139   }
    140 }
    141 
    142 void BrowserBlacklistBeaconSetup() {
    143   base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER,
    144                                            blacklist::kRegistryBeaconPath,
    145                                            KEY_QUERY_VALUE | KEY_SET_VALUE);
    146 
    147   // No point in trying to continue if the registry key isn't valid.
    148   if (!blacklist_registry_key.Valid())
    149     return;
    150 
    151   // Record the results of the last blacklist setup.
    152   DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
    153   blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state);
    154 
    155   if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
    156     // The blacklist setup didn't crash, so we report if it was enabled or not.
    157     if (blacklist::IsBlacklistInitialized()) {
    158       RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY);
    159     } else {
    160       // The only way for the blacklist to be enabled, but not fully
    161       // initialized is if the thunk setup failed. See blacklist.cc
    162       // for more details.
    163       RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED);
    164     }
    165 
    166     // Regardless of if the blacklist was fully enabled or not, report how many
    167     // times we had to try to set it up.
    168     DWORD attempt_count = 0;
    169     blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount,
    170                                        &attempt_count);
    171     UMA_HISTOGRAM_COUNTS_100("Blacklist.RetryAttempts.Success", attempt_count);
    172   } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) {
    173     // We can set the state to disabled without checking that the maximum number
    174     // of attempts was exceeded because blacklist.cc has already done this.
    175     RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED);
    176     blacklist_registry_key.WriteValue(blacklist::kBeaconState,
    177                                       blacklist::BLACKLIST_DISABLED);
    178   } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) {
    179     RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED);
    180   }
    181 
    182   // Find the last recorded blacklist version.
    183   base::string16 blacklist_version;
    184   blacklist_registry_key.ReadValue(blacklist::kBeaconVersion,
    185                                    &blacklist_version);
    186 
    187   if (blacklist_version != TEXT(CHROME_VERSION_STRING)) {
    188     // The blacklist hasn't been enabled for this version yet, so enable it
    189     // and reset the failure count to zero.
    190     LONG set_version = blacklist_registry_key.WriteValue(
    191         blacklist::kBeaconVersion,
    192         TEXT(CHROME_VERSION_STRING));
    193 
    194     LONG set_state = blacklist_registry_key.WriteValue(
    195         blacklist::kBeaconState,
    196         blacklist::BLACKLIST_ENABLED);
    197 
    198     blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount,
    199                                       static_cast<DWORD>(0));
    200 
    201     // Only report the blacklist as getting setup when both registry writes
    202     // succeed, since otherwise the blacklist wasn't properly setup.
    203     if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS)
    204       RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED);
    205   }
    206 }
    207