// 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_ENUMERATE_MODULES_MODEL_WIN_H_ #define CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_ #include <utility> #include <vector> #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/singleton.h" #include "base/strings/string16.h" #include "base/timer/timer.h" #include "content/public/browser/browser_thread.h" #include "url/gurl.h" class EnumerateModulesModel; namespace base { class FilePath; class ListValue; } // A helper class that implements the enumerate module functionality on the File // thread. class ModuleEnumerator : public base::RefCountedThreadSafe<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, }; // Which Windows OS is affected. enum OperatingSystem { ALL = -1, XP = 1 << 0, }; // The structure we populate when enumerating modules. struct 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 signer of the digital certificate for the module. base::string16 digital_signer; // The help tips bitmask. RecommendedAction recommended_action; // The duplicate count within each category of modules. int duplicate_count; // Whether this module has been normalized (necessary before checking it // against blacklist). bool normalized; }; // A vector typedef of all modules enumerated. typedef std::vector<Module> ModulesVector; // A structure we populate with the blacklist entries. struct BlacklistEntry { const char* filename; const char* location; const char* desc_or_signer; const char* version_from; // Version where conflict started. const char* version_to; // First version that works. OperatingSystem os; // Bitmask, representing what OS this entry applies to. RecommendedAction help_tip; }; // 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); // A static function that checks whether |module| has been |blacklisted|. static ModuleStatus Match(const Module& module, const BlacklistEntry& blacklisted); 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 // (unless we are in limited_mode, see below), and will notify when done // through the MODULE_LIST_ENUMERATED notification. // The process will also send MODULE_INCOMPATIBILITY_BADGE_CHANGE to let // observers know when it is time to update the wrench menu badge. // When in |limited_mode|, this function will not leverage the File thread // to run asynchronously and will therefore block until scanning is done // (and will also not send out any notifications). void ScanNow(ModulesVector* list, bool limited_mode); private: FRIEND_TEST_ALL_PREFIXES(EnumerateModulesTest, CollapsePath); // The (currently) hard coded blacklist of known bad modules. static const BlacklistEntry kModuleBlacklist[]; // This function does the actual file scanning work on the FILE thread (or // block the main thread when in limited_mode). It enumerates all loaded // modules in the process and other modules of interest, such as the // registered Winsock LSP modules and stores them in |enumerated_modules_|. // It then normalizes the module info and matches them against a blacklist // of known bad modules. Finally, it calls ReportBack to let the observer // know we are done. void ScanImpl(); // Enumerate all modules loaded into the Chrome process. void EnumerateLoadedModules(); // Enumerate all registered Windows shell extensions. void EnumerateShellExtensions(); // Enumerate all registered Winsock LSP modules. void EnumerateWinsockModules(); // Reads the registered shell extensions found under |parent| key in the // registry. 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(); // For a given |module|, collapse the path from c:\windows to %systemroot%, // based on the |path_mapping_| vector. void CollapsePath(Module* module); // Takes each module in the |enumerated_modules_| vector and matches it // against a fixed blacklist of bad and suspected bad modules. void MatchAgainstBlacklist(); // This function executes on the UI thread when the scanning and matching // process is done. It notifies the observer. void ReportBack(); // Given a filename, returns the Subject (who signed it) retrieved from // the digital signature (Authenticode). base::string16 GetSubjectNameFromDigitalSignature( const base::FilePath& filename); // The typedef for the vector that maps a regular file path to %env_var%. typedef std::vector< std::pair<base::string16, base::string16> > PathMapping; // The vector of paths to %env_var%, used to account for differences in // where people keep there files, c:\windows vs. d:\windows, etc. PathMapping path_mapping_; // The vector containing all the enumerated modules (loaded and modules of // interest). ModulesVector* enumerated_modules_; // The observer, who needs to be notified when we are done. EnumerateModulesModel* observer_; // See limited_mode below. bool limited_mode_; // The thread that we need to call back on to report that we are done. content::BrowserThread::ID callback_thread_id_; 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. // // This class can be used on the UI thread as it asynchronously offloads the // file work over to the FILE thread and reports back to the caller with a // notification. 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. }; static EnumerateModulesModel* GetInstance(); // Record via UMA what the user selected. static void RecordLearnMoreStat(bool from_menu); // 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 { return suspected_bad_modules_detected_; } // 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 { return confirmed_bad_modules_detected_; } // Returns how many modules to notify the user about. int modules_to_notify_about() const { return modules_to_notify_about_; } // Set to true when we the scanning process can not rely on certain Chrome // services to exists. void set_limited_mode(bool limited_mode) { limited_mode_ = limited_mode; } // Checks to see if a scanning task should be started and sets one off, if so. void MaybePostScanningTask(); // Asynchronously start the scan for the loaded module list, except when in // limited_mode (in which case it blocks). void ScanNow(); // Gets the whole module list as a ListValue. base::ListValue* GetModuleList() const; // Gets the Help Center URL for the first *notable* conflict module that we've // elected to notify the user about. GURL GetFirstNotableConflict(); private: friend struct DefaultSingletonTraits<EnumerateModulesModel>; friend class ModuleEnumerator; EnumerateModulesModel(); virtual ~EnumerateModulesModel(); // Called on the UI thread when the helper class is done scanning. void DoneScanning(); // Constructs a Help Center article URL for help with a particular module. // The module must have the SEE_LINK attribute for |recommended_action| set, // otherwise this returns a blank string. GURL ConstructHelpCenterUrl(const ModuleEnumerator::Module& module) const; // The vector containing all the modules enumerated. Will be normalized and // any bad modules will be marked. ModuleEnumerator::ModulesVector enumerated_modules_; // The object responsible for enumerating the modules on the File thread. scoped_refptr<ModuleEnumerator> module_enumerator_; // When this singleton object is constructed we go and fire off this timer to // start scanning for modules after a certain amount of time has passed. base::OneShotTimer<EnumerateModulesModel> check_modules_timer_; // While normally |false|, this mode can be set to indicate that the scanning // process should not rely on certain services normally available to Chrome, // such as the resource bundle and the notification system, not to mention // having multiple threads. This mode is useful during diagnostics, which // runs without firing up all necessary Chrome services first. bool limited_mode_; // True if we are currently scanning for modules. bool scanning_; // Whether the conflict notification has been acknowledged by the user. bool conflict_notification_acknowledged_; // The number of confirmed bad modules (not including suspected bad ones) // found during last scan. int confirmed_bad_modules_detected_; // The number of bad modules the user needs to be aggressively notified about. int modules_to_notify_about_; // The number of suspected bad modules (not including confirmed bad ones) // found during last scan. int suspected_bad_modules_detected_; DISALLOW_COPY_AND_ASSIGN(EnumerateModulesModel); }; #endif // CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_