blob: 7be732ea3b9cf7ac7804593782724040e8e80ad7 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WIN_ENUMERATE_MODULES_MODEL_H_
#define CHROME_BROWSER_WIN_ENUMERATE_MODULES_MODEL_H_
#include <memory>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
#include "base/timer/timer.h"
#include "chrome/browser/conflicts/module_info_util_win.h"
#include "content/public/browser/browser_thread.h"
#include "url/gurl.h"
class EnumerateModulesModel;
namespace base {
class ListValue;
}
// A helper class that implements the enumerate module functionality on the FILE
// thread. Not to be used directly.
// TODO(chrisha): Move this to a separate .h and .cc.
class ModuleEnumerator {
public:
// What type of module we are dealing with. Loaded modules are modules we
// detect as loaded in the process at the time of scanning. The others are
// modules of interest and may or may not be loaded in the process at the
// time of scan.
enum ModuleType {
LOADED_MODULE = 1 << 0,
SHELL_EXTENSION = 1 << 1,
WINSOCK_MODULE_REGISTRATION = 1 << 2,
};
// The blacklist status of the module. Suspected Bad modules have been
// partially matched (ie. name matches and location, but not description)
// whereas Confirmed Bad modules have been identified further (ie.
// AuthentiCode signer matches).
enum ModuleStatus {
// This is returned by the matching function when comparing against the
// blacklist and the module does not match the current entry in the
// blacklist.
NOT_MATCHED,
// The module is not on the blacklist. Assume it is good.
GOOD,
// Module is a suspected bad module.
SUSPECTED_BAD,
// Module is a bad bad dog.
CONFIRMED_BAD,
};
// A bitmask with the possible resolutions for bad modules.
enum RecommendedAction {
NONE = 0,
INVESTIGATING = 1 << 0,
UNINSTALL = 1 << 1,
DISABLE = 1 << 2,
UPDATE = 1 << 3,
SEE_LINK = 1 << 4,
NOTIFY_USER = 1 << 5,
};
// The structure we populate when enumerating modules.
struct Module {
Module();
Module(const Module& rhs);
// Constructor exposed for unittesting.
Module(ModuleType type,
ModuleStatus status,
const base::string16& location,
const base::string16& name,
const base::string16& product_name,
const base::string16& description,
const base::string16& version,
RecommendedAction recommended_action);
~Module();
// The type of module found
ModuleType type;
// The module status (benign/bad/etc).
ModuleStatus status;
// The module path, not including filename.
base::string16 location;
// The name of the module (filename).
base::string16 name;
// The name of the product the module belongs to.
base::string16 product_name;
// The module file description.
base::string16 description;
// The module version.
base::string16 version;
// The help tips bitmask.
RecommendedAction recommended_action;
// The duplicate count within each category of modules.
int duplicate_count;
// The certificate info for the module.
CertificateInfo cert_info;
};
// A vector typedef of all modules enumerated.
typedef std::vector<Module> ModulesVector;
// A static function that normalizes the module information in the |module|
// struct. Module information needs to be normalized before comparing against
// the blacklist. This is because the same module can be described in many
// different ways, ie. file paths can be presented in long/short name form,
// and are not case sensitive on Windows. Also, the version string returned
// can include appended text, which we don't want to use during comparison
// against the blacklist.
static void NormalizeModule(Module* module);
// Constructs a ModuleEnumerator that will notify the provided |observer| once
// enumeration is complete. |observer| must outlive the ModuleEnumerator.
explicit ModuleEnumerator(EnumerateModulesModel* observer);
~ModuleEnumerator();
// Start scanning the loaded module list (if a scan is not already in
// progress). This function does not block while reading the module list and
// will notify when done by calling the DoneScanning method of |observer_|.
void ScanNow(ModulesVector* list);
// Sets |per_module_delay_| to zero, causing the modules to be inspected
// in realtime.
void SetPerModuleDelayToZero();
private:
FRIEND_TEST_ALL_PREFIXES(EnumerateModulesTest, CollapsePath);
// This function posts a task to enumerate all modules asynchronously. Once
// the list of module filenames is populated, a delayed task is posted to scan
// the first module.
void ScanImplStart();
// Inspects the module in |enumerated_modules_| at the given |index|. Gets
// module information, normalizes it, and collapses the path. This is an
// expensive operation and non-critical. Posts a delayed task to ScanImplDelay
// for the next module. When all modules are finished forwards directly to
// ScanImplFinish.
void ScanImplModule(size_t index);
// Collects metrics and notifies the observer that the enumeration is complete
// by invoking DoneScanning on the UI thread.
void ScanImplFinish();
// Enumerate all modules loaded into the Chrome process. Creates empty
// entries in |enumerated_modules_| with a populated |location| field.
void EnumerateLoadedModules();
// Enumerate all registered Windows shell extensions. Creates empty
// entries in |enumerated_modules_| with a populated |location| field.
void EnumerateShellExtensions();
// Enumerate all registered Winsock LSP modules. Creates empty
// entries in |enumerated_modules_| with a populated |location| field.
void EnumerateWinsockModules();
// Reads the registered shell extensions found under |parent| key in the
// registry. Creates empty entries in |enumerated_modules_| with a populated
// |location| field.
void ReadShellExtensions(HKEY parent);
// Given a |module|, initializes the structure and loads additional
// information using the location field of the module.
void PopulateModuleInformation(Module* module);
// Checks the module list to see if a |module| of the same type, location
// and name has been added before and if so, increments its duplication
// counter. If it doesn't appear in the list, it is added.
void AddToListWithoutDuplicating(const Module&);
// Builds up a vector of path values mapping to environment variable,
// with pairs like [c:\windows\, %systemroot%]. This is later used to
// collapse paths like c:\windows\system32 into %systemroot%\system32, which
// we can use for comparison against our blacklist (which uses only env vars).
// NOTE: The vector will not contain an exhaustive list of environment
// variables, only the ones currently found on the blacklist or ones that are
// likely to appear there.
void PreparePathMappings();
// Reports (via UMA) a handful of high-level metrics regarding third party
// modules in this process. Called by ScanImplFinish.
void ReportThirdPartyMetrics();
// The TaskRunner to perform work in the background.
const scoped_refptr<base::TaskRunner> background_task_runner_;
// The vector of paths to %env_var%, used to account for differences in
// where people keep there files, c:\windows vs. d:\windows, etc.
StringMapping path_mapping_;
// The vector containing all the enumerated modules (loaded and modules of
// interest).
ModulesVector* enumerated_modules_;
// The observer, which needs to be notified when the scan is complete.
EnumerateModulesModel* observer_;
// The delay that is observed between module inspection tasks. This is
// currently 1 second, which means it takes several minutes to iterate over
// all modules on average.
base::TimeDelta per_module_delay_;
// The amount of time taken for on-disk module inspection. Reported in
// ScanImplFinish.
base::TimeDelta enumeration_inspection_time_;
// The total amount of time taken for module enumeration. Reported in
// ScanImplFinish.
base::TimeDelta enumeration_total_time_;
DISALLOW_COPY_AND_ASSIGN(ModuleEnumerator);
};
// This is a singleton class that enumerates all modules loaded into Chrome,
// both currently loaded modules (called DLLs on Windows) and modules 'of
// interest', such as WinSock LSP modules. This class also marks each module
// as benign or suspected bad or outright bad, using a supplied blacklist that
// is currently hard-coded.
//
// To use this class, grab the singleton pointer and call ScanNow().
// Then wait to get notified through MODULE_LIST_ENUMERATED when the list is
// ready.
//
// The member functions of this class may only be used from the UI thread. The
// bulk of the work is actually performed asynchronously in TaskScheduler with
// CONTINUE_ON_SHUTDOWN semantics, as the WinCrypt functions can effectively
// block arbitrarily during shutdown.
//
// TODO(chrisha): If this logic is ever extended to other platforms, then make
// this file generic for all platforms, and remove the precompiler logic in
// app_menu_icon_controller.*.
class EnumerateModulesModel {
public:
// UMA histogram constants.
enum UmaModuleConflictHistogramOptions {
ACTION_BUBBLE_SHOWN = 0,
ACTION_BUBBLE_LEARN_MORE,
ACTION_MENU_LEARN_MORE,
ACTION_BOUNDARY, // Must be the last value.
};
// Observer class used to determine when a scan has completed and when any
// associated UI elements have been dismissed.
class Observer {
public:
// Invoked when EnumerateModulesModel has completed a scan of modules.
virtual void OnScanCompleted() {}
// Invoked when a user has acknowledged incompatible modules found in a
// module scan.
virtual void OnConflictsAcknowledged() {}
protected:
virtual ~Observer() = default;
};
// Returns the singleton instance of this class.
static EnumerateModulesModel* GetInstance();
// Adds an |observer| to the enumerator. Callbacks will occur on the UI
// thread.
void AddObserver(Observer* observer);
// Removes an |observer| from the enumerator.
void RemoveObserver(Observer* observer);
// Returns true if we should show the conflict notification. The conflict
// notification is only shown once during the lifetime of the process.
bool ShouldShowConflictWarning() const;
// Called when the user has acknowledged the conflict notification.
void AcknowledgeConflictNotification();
// Returns the number of suspected bad modules found in the last scan.
// Returns 0 if no scan has taken place yet.
int suspected_bad_modules_detected() const;
// Returns the number of confirmed bad modules found in the last scan.
// Returns 0 if no scan has taken place yet.
int confirmed_bad_modules_detected() const;
// Returns how many modules to notify the user about.
int modules_to_notify_about() const;
// Checks to see if a scanning task should be started and sets one off, if so.
// This will cause ScanNow to be invoked in background mode.
void MaybePostScanningTask();
// Asynchronously start the scan for the loaded module list. If
// |background_mode| is true the scan will happen slowly over a process of
// minutes, spread across dozens or even hundreds of delayed tasks. Otherwise
// the processing will occur in a single task.
void ScanNow(bool background_mode);
// Gets the whole module list as a ListValue.
std::unique_ptr<base::ListValue> GetModuleList();
// Returns the site to which the user should be taken when the conflict bubble
// or app menu item is clicked. For now this is simply chrome://conflicts,
// which contains detailed information about conflicts. Returns an empty URL
// if there are no conficts. May only be called on UI thread.
GURL GetConflictUrl();
private:
friend class ModuleEnumerator;
// Private to enforce singleton nature of this class.
EnumerateModulesModel();
~EnumerateModulesModel();
// Called on the UI thread when the helper class is done scanning. The
// ModuleEnumerator that calls this must not do any work after causing this
// function to be called, as the EnumerateModulesModel may delete the
// ModuleEnumerator.
void DoneScanning();
// The vector containing all the modules enumerated. Will be normalized and
// any bad modules will be marked. Written to from the background TaskRunner
// by the |module_enumerator_|, read from on the UI thread by this class.
ModuleEnumerator::ModulesVector enumerated_modules_;
// The object responsible for enumerating the modules on a background
// TaskRunner. Only accessed from the UI thread.
std::unique_ptr<ModuleEnumerator> module_enumerator_;
// Whether the conflict notification has been acknowledged by the user. Only
// modified on the UI thread.
bool conflict_notification_acknowledged_;
// The number of confirmed bad modules (not including suspected bad ones)
// found during last scan. Only modified on the UI thread.
int confirmed_bad_modules_detected_;
// The number of bad modules the user needs to be aggressively notified about.
// Only modified on the UI thread.
int modules_to_notify_about_;
// The number of suspected bad modules (not including confirmed bad ones)
// found during last scan. Only modified on the UI thread.
int suspected_bad_modules_detected_;
base::ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(EnumerateModulesModel);
};
#endif // CHROME_BROWSER_WIN_ENUMERATE_MODULES_MODEL_H_