Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1 | // Copyright (c) 2011 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 | #ifndef CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_ |
| 6 | #define CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_ |
| 7 | |
| 8 | #include <utility> |
| 9 | #include <vector> |
| 10 | |
| 11 | #include "base/gtest_prod_util.h" |
| 12 | #include "base/memory/ref_counted.h" |
| 13 | #include "base/memory/singleton.h" |
| 14 | #include "base/string16.h" |
| 15 | #include "base/timer.h" |
| 16 | #include "content/public/browser/browser_thread.h" |
| 17 | #include "googleurl/src/gurl.h" |
| 18 | |
| 19 | class EnumerateModulesModel; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 20 | |
| 21 | namespace base { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame^] | 22 | class FilePath; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 23 | class ListValue; |
| 24 | } |
| 25 | |
| 26 | // A helper class that implements the enumerate module functionality on the File |
| 27 | // thread. |
| 28 | class ModuleEnumerator : public base::RefCountedThreadSafe<ModuleEnumerator> { |
| 29 | public: |
| 30 | // What type of module we are dealing with. Loaded modules are modules we |
| 31 | // detect as loaded in the process at the time of scanning. The others are |
| 32 | // modules of interest and may or may not be loaded in the process at the |
| 33 | // time of scan. |
| 34 | enum ModuleType { |
| 35 | LOADED_MODULE = 1 << 0, |
| 36 | SHELL_EXTENSION = 1 << 1, |
| 37 | WINSOCK_MODULE_REGISTRATION = 1 << 2, |
| 38 | }; |
| 39 | |
| 40 | // The blacklist status of the module. Suspected Bad modules have been |
| 41 | // partially matched (ie. name matches and location, but not description) |
| 42 | // whereas Confirmed Bad modules have been identified further (ie. |
| 43 | // AuthentiCode signer matches). |
| 44 | enum ModuleStatus { |
| 45 | // This is returned by the matching function when comparing against the |
| 46 | // blacklist and the module does not match the current entry in the |
| 47 | // blacklist. |
| 48 | NOT_MATCHED, |
| 49 | // The module is not on the blacklist. Assume it is good. |
| 50 | GOOD, |
| 51 | // Module is a suspected bad module. |
| 52 | SUSPECTED_BAD, |
| 53 | // Module is a bad bad dog. |
| 54 | CONFIRMED_BAD, |
| 55 | }; |
| 56 | |
| 57 | // A bitmask with the possible resolutions for bad modules. |
| 58 | enum RecommendedAction { |
| 59 | NONE = 0, |
| 60 | INVESTIGATING = 1 << 0, |
| 61 | UNINSTALL = 1 << 1, |
| 62 | DISABLE = 1 << 2, |
| 63 | UPDATE = 1 << 3, |
| 64 | SEE_LINK = 1 << 4, |
| 65 | }; |
| 66 | |
| 67 | // The structure we populate when enumerating modules. |
| 68 | struct Module { |
| 69 | // The type of module found |
| 70 | ModuleType type; |
| 71 | // The module status (benign/bad/etc). |
| 72 | ModuleStatus status; |
| 73 | // The module path, not including filename. |
| 74 | string16 location; |
| 75 | // The name of the module (filename). |
| 76 | string16 name; |
| 77 | // The name of the product the module belongs to. |
| 78 | string16 product_name; |
| 79 | // The module file description. |
| 80 | string16 description; |
| 81 | // The module version. |
| 82 | string16 version; |
| 83 | // The signer of the digital certificate for the module. |
| 84 | string16 digital_signer; |
| 85 | // The help tips bitmask. |
| 86 | RecommendedAction recommended_action; |
| 87 | // The duplicate count within each category of modules. |
| 88 | int duplicate_count; |
| 89 | // Whether this module has been normalized (necessary before checking it |
| 90 | // against blacklist). |
| 91 | bool normalized; |
| 92 | }; |
| 93 | |
| 94 | // A vector typedef of all modules enumerated. |
| 95 | typedef std::vector<Module> ModulesVector; |
| 96 | |
| 97 | // A structure we populate with the blacklist entries. |
| 98 | struct BlacklistEntry { |
| 99 | const char* filename; |
| 100 | const char* location; |
| 101 | const char* desc_or_signer; |
| 102 | const char* version_from; // Version where conflict started. |
| 103 | const char* version_to; // First version that works. |
| 104 | RecommendedAction help_tip; |
| 105 | }; |
| 106 | |
| 107 | // A static function that normalizes the module information in the |module| |
| 108 | // struct. Module information needs to be normalized before comparing against |
| 109 | // the blacklist. This is because the same module can be described in many |
| 110 | // different ways, ie. file paths can be presented in long/short name form, |
| 111 | // and are not case sensitive on Windows. Also, the version string returned |
| 112 | // can include appended text, which we don't want to use during comparison |
| 113 | // against the blacklist. |
| 114 | static void NormalizeModule(Module* module); |
| 115 | |
| 116 | // A static function that checks whether |module| has been |blacklisted|. |
| 117 | static ModuleStatus Match(const Module& module, |
| 118 | const BlacklistEntry& blacklisted); |
| 119 | |
| 120 | explicit ModuleEnumerator(EnumerateModulesModel* observer); |
| 121 | ~ModuleEnumerator(); |
| 122 | |
| 123 | // Start scanning the loaded module list (if a scan is not already in |
| 124 | // progress). This function does not block while reading the module list |
| 125 | // (unless we are in limited_mode, see below), and will notify when done |
| 126 | // through the MODULE_LIST_ENUMERATED notification. |
| 127 | // The process will also send MODULE_INCOMPATIBILITY_BADGE_CHANGE to let |
| 128 | // observers know when it is time to update the wrench menu badge. |
| 129 | // When in |limited_mode|, this function will not leverage the File thread |
| 130 | // to run asynchronously and will therefore block until scanning is done |
| 131 | // (and will also not send out any notifications). |
| 132 | void ScanNow(ModulesVector* list, bool limited_mode); |
| 133 | |
| 134 | private: |
| 135 | FRIEND_TEST_ALL_PREFIXES(EnumerateModulesTest, CollapsePath); |
| 136 | |
| 137 | // The (currently) hard coded blacklist of known bad modules. |
| 138 | static const BlacklistEntry kModuleBlacklist[]; |
| 139 | |
| 140 | // This function does the actual file scanning work on the FILE thread (or |
| 141 | // block the main thread when in limited_mode). It enumerates all loaded |
| 142 | // modules in the process and other modules of interest, such as the |
| 143 | // registered Winsock LSP modules and stores them in |enumerated_modules_|. |
| 144 | // It then normalizes the module info and matches them against a blacklist |
| 145 | // of known bad modules. Finally, it calls ReportBack to let the observer |
| 146 | // know we are done. |
| 147 | void ScanImpl(); |
| 148 | |
| 149 | // Enumerate all modules loaded into the Chrome process. |
| 150 | void EnumerateLoadedModules(); |
| 151 | |
| 152 | // Enumerate all registered Windows shell extensions. |
| 153 | void EnumerateShellExtensions(); |
| 154 | |
| 155 | // Enumerate all registered Winsock LSP modules. |
| 156 | void EnumerateWinsockModules(); |
| 157 | |
| 158 | // Reads the registered shell extensions found under |parent| key in the |
| 159 | // registry. |
| 160 | void ReadShellExtensions(HKEY parent); |
| 161 | |
| 162 | // Given a |module|, initializes the structure and loads additional |
| 163 | // information using the location field of the module. |
| 164 | void PopulateModuleInformation(Module* module); |
| 165 | |
| 166 | // Checks the module list to see if a |module| of the same type, location |
| 167 | // and name has been added before and if so, increments its duplication |
| 168 | // counter. If it doesn't appear in the list, it is added. |
| 169 | void AddToListWithoutDuplicating(const Module&); |
| 170 | |
| 171 | // Builds up a vector of path values mapping to environment variable, |
| 172 | // with pairs like [c:\windows\, %systemroot%]. This is later used to |
| 173 | // collapse paths like c:\windows\system32 into %systemroot%\system32, which |
| 174 | // we can use for comparison against our blacklist (which uses only env vars). |
| 175 | // NOTE: The vector will not contain an exhaustive list of environment |
| 176 | // variables, only the ones currently found on the blacklist or ones that are |
| 177 | // likely to appear there. |
| 178 | void PreparePathMappings(); |
| 179 | |
| 180 | // For a given |module|, collapse the path from c:\windows to %systemroot%, |
| 181 | // based on the |path_mapping_| vector. |
| 182 | void CollapsePath(Module* module); |
| 183 | |
| 184 | // Takes each module in the |enumerated_modules_| vector and matches it |
| 185 | // against a fixed blacklist of bad and suspected bad modules. |
| 186 | void MatchAgainstBlacklist(); |
| 187 | |
| 188 | // This function executes on the UI thread when the scanning and matching |
| 189 | // process is done. It notifies the observer. |
| 190 | void ReportBack(); |
| 191 | |
| 192 | // Given a filename, returns the Subject (who signed it) retrieved from |
| 193 | // the digital signature (Authenticode). |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame^] | 194 | string16 GetSubjectNameFromDigitalSignature(const base::FilePath& filename); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 195 | |
| 196 | // The typedef for the vector that maps a regular file path to %env_var%. |
| 197 | typedef std::vector< std::pair<string16, string16> > PathMapping; |
| 198 | |
| 199 | // The vector of paths to %env_var%, used to account for differences in |
| 200 | // where people keep there files, c:\windows vs. d:\windows, etc. |
| 201 | PathMapping path_mapping_; |
| 202 | |
| 203 | // The vector containing all the enumerated modules (loaded and modules of |
| 204 | // interest). |
| 205 | ModulesVector* enumerated_modules_; |
| 206 | |
| 207 | // The observer, who needs to be notified when we are done. |
| 208 | EnumerateModulesModel* observer_; |
| 209 | |
| 210 | // See limited_mode below. |
| 211 | bool limited_mode_; |
| 212 | |
| 213 | // The thread that we need to call back on to report that we are done. |
| 214 | content::BrowserThread::ID callback_thread_id_; |
| 215 | |
| 216 | DISALLOW_COPY_AND_ASSIGN(ModuleEnumerator); |
| 217 | }; |
| 218 | |
| 219 | // This is a singleton class that enumerates all modules loaded into Chrome, |
| 220 | // both currently loaded modules (called DLLs on Windows) and modules 'of |
| 221 | // interest', such as WinSock LSP modules. This class also marks each module |
| 222 | // as benign or suspected bad or outright bad, using a supplied blacklist that |
| 223 | // is currently hard-coded. |
| 224 | // |
| 225 | // To use this class, grab the singleton pointer and call ScanNow(). |
| 226 | // Then wait to get notified through MODULE_LIST_ENUMERATED when the list is |
| 227 | // ready. |
| 228 | // |
| 229 | // This class can be used on the UI thread as it asynchronously offloads the |
| 230 | // file work over to the FILE thread and reports back to the caller with a |
| 231 | // notification. |
| 232 | class EnumerateModulesModel { |
| 233 | public: |
| 234 | static EnumerateModulesModel* GetInstance(); |
| 235 | |
| 236 | // Returns true if we should show the conflict notification. The conflict |
| 237 | // notification is only shown once during the lifetime of the process. |
| 238 | bool ShouldShowConflictWarning() const; |
| 239 | |
| 240 | // Called when the user has acknowledged the conflict notification. |
| 241 | void AcknowledgeConflictNotification(); |
| 242 | |
| 243 | // Returns the number of suspected bad modules found in the last scan. |
| 244 | // Returns 0 if no scan has taken place yet. |
| 245 | int suspected_bad_modules_detected() const { |
| 246 | return suspected_bad_modules_detected_; |
| 247 | } |
| 248 | |
| 249 | // Returns the number of confirmed bad modules found in the last scan. |
| 250 | // Returns 0 if no scan has taken place yet. |
| 251 | int confirmed_bad_modules_detected() const { |
| 252 | return confirmed_bad_modules_detected_; |
| 253 | } |
| 254 | |
| 255 | // Set to true when we the scanning process can not rely on certain Chrome |
| 256 | // services to exists. |
| 257 | void set_limited_mode(bool limited_mode) { |
| 258 | limited_mode_ = limited_mode; |
| 259 | } |
| 260 | |
| 261 | // Asynchronously start the scan for the loaded module list, except when in |
| 262 | // limited_mode (in which case it blocks). |
| 263 | void ScanNow(); |
| 264 | |
| 265 | // Gets the whole module list as a ListValue. |
| 266 | base::ListValue* GetModuleList() const; |
| 267 | |
| 268 | private: |
| 269 | friend struct DefaultSingletonTraits<EnumerateModulesModel>; |
| 270 | friend class ModuleEnumerator; |
| 271 | |
| 272 | EnumerateModulesModel(); |
| 273 | virtual ~EnumerateModulesModel(); |
| 274 | |
| 275 | // Called on the UI thread when the helper class is done scanning. |
| 276 | void DoneScanning(); |
| 277 | |
| 278 | // Constructs a Help Center article URL for help with a particular module. |
| 279 | // The module must have the SEE_LINK attribute for |recommended_action| set, |
| 280 | // otherwise this returns a blank string. |
| 281 | GURL ConstructHelpCenterUrl(const ModuleEnumerator::Module& module) const; |
| 282 | |
| 283 | // The vector containing all the modules enumerated. Will be normalized and |
| 284 | // any bad modules will be marked. |
| 285 | ModuleEnumerator::ModulesVector enumerated_modules_; |
| 286 | |
| 287 | // The object responsible for enumerating the modules on the File thread. |
| 288 | scoped_refptr<ModuleEnumerator> module_enumerator_; |
| 289 | |
| 290 | // When this singleton object is constructed we go and fire off this timer to |
| 291 | // start scanning for modules after a certain amount of time has passed. |
| 292 | base::OneShotTimer<EnumerateModulesModel> check_modules_timer_; |
| 293 | |
| 294 | // While normally |false|, this mode can be set to indicate that the scanning |
| 295 | // process should not rely on certain services normally available to Chrome, |
| 296 | // such as the resource bundle and the notification system, not to mention |
| 297 | // having multiple threads. This mode is useful during diagnostics, which |
| 298 | // runs without firing up all necessary Chrome services first. |
| 299 | bool limited_mode_; |
| 300 | |
| 301 | // True if we are currently scanning for modules. |
| 302 | bool scanning_; |
| 303 | |
| 304 | // Whether the conflict notification has been acknowledged by the user. |
| 305 | bool conflict_notification_acknowledged_; |
| 306 | |
| 307 | // The number of confirmed bad modules (not including suspected bad ones) |
| 308 | // found during last scan. |
| 309 | int confirmed_bad_modules_detected_; |
| 310 | |
| 311 | // The number of suspected bad modules (not including confirmed bad ones) |
| 312 | // found during last scan. |
| 313 | int suspected_bad_modules_detected_; |
| 314 | |
| 315 | DISALLOW_COPY_AND_ASSIGN(EnumerateModulesModel); |
| 316 | }; |
| 317 | |
| 318 | #endif // CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_ |