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