1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WIN_CONFLICTS_MODULE_BLOCKLIST_CACHE_UPDATER_H_
#define CHROME_BROWSER_WIN_CONFLICTS_MODULE_BLOCKLIST_CACHE_UPDATER_H_
#include <vector>
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/hash/md5.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chrome/browser/win/conflicts/module_database_observer.h"
#include "chrome/browser/win/conflicts/proto/module_list.pb.h"
#include "chrome/chrome_elf/third_party_dlls/packed_list_format.h"
class ModuleListFilter;
struct CertificateInfo;
namespace base {
class SequencedTaskRunner;
}
// This class is responsible for maintaining the module blocklist cache, which
// is used by chrome_elf.dll to determine which module to block from loading
// into the process.
//
// The cache is updated everytime the module database becomes idle and at least
// one module must be added to the cache.
//
//
// Additional implementation details about the module blocklist cache file:
//
// Because the file is written under the User Data directory, it is not possible
// for chrome_elf to find it by itself (see https://crbug.com/748949). So the
// path to the file is written into a registry key, which solves the problem by
// making the already expanded path available.
//
// A consequence of this solution is that in some circumstances, multiple
// browser process will race to write the path to their blocklist file into a
// single registry key because the same registry key is shared between all User
// Data directories.
//
// This means that a browser instance launch using one User Data directory can
// potentially use the cache from another User Data directory instead of their
// own.
//
// This is acceptable given that all the caches contains more or less the same
// information and are interchangeable. Also, updates to the cache file and to
// the registry are atomic, guaranteeing that chrome_elf always reads a valid
// blocklist.
class ModuleBlocklistCacheUpdater : public ModuleDatabaseObserver {
public:
// A decision that explains whether or not a module is to be blocked. This
// decision is made with respect to the logic in the currently running
// executable and the current version of the module list. It may not
// correspond with the blocking decisions that were enforced by the blocking
// logic in chrome_elf during early startup. Those decisions are cached from
// the previous browser launch which may be with respect to (a) an entirely
// different version of the browser and/or (b) an entirely different version
// of the module list.
//
// Note that this enum is very similar to the ModuleWarningDecision in
// IncompatibleApplicationsUpdater. This is done so that it is easier to keep
// the 2 features separate, as they can be independently enabled/disabled.
enum class ModuleBlockingDecision {
// No decision was taken yet for the module.
kUnknown,
// Detailed reasons why modules will be allowed to load in subsequent
// startups.
// A shell extension or IME that is not loaded in the process yet.
kNotLoaded,
// A module that is loaded into a process type where third-party modules are
// explicitly allowed.
kAllowedInProcessType,
// Input method editors are allowed.
kAllowedIME,
// Allowed because the certificate's subject of the module matches the
// certificate's subject of the executable. The certificate is not
// validated.
kAllowedSameCertificate,
// Allowed because the path of the executable is the parent of the path of
// the module. Only used in non-official builds.
kAllowedSameDirectory,
// Allowed because it is signed by Microsoft. The certificate is not
// validated.
kAllowedMicrosoft,
// Explicitly allowed by the Module List component.
kAllowedAllowlisted,
// Module analysis was interrupted using DisableModuleAnalysis(). This
// module will not be added to the cache.
kNotAnalyzed,
// New "allowed" reasons should be added here!
// Unwanted, but allowed to load by the Module List component. This is
// usually because blocking the module would cause more stability issues
// than allowing it. If the IncompatibleApplicationsWarning feature is
// enabled, this module will cause a warning if it can be tied back to an
// installed application.
kTolerated,
// Detailed reasons why modules will not be allowed to load in subsequent
// startups.
// The module will be blocked because it is explicitly listed in the module
// blocklist.
kDisallowedExplicit,
// The module will be implicitly blocked because it is not otherwise
// allowed.
kDisallowedImplicit,
// New "disallowed" reasons should be added here!
};
struct ModuleBlockingState {
// Whether or not the module was in the blocklist cache that existed at
// startup.
bool was_in_blocklist_cache = false;
// Whether or not the module was ever actively blocked from loading during
// this session.
bool was_blocked = false;
// Whether or not the module ever loaded during this session. Usually this
// means that the module is currently loaded, but it's possible for DLLs to
// subsequently be unloaded at runtime.
bool was_loaded = false;
// The current blocking decision. This is synced to the cache and will be
// applied at the next startup.
ModuleBlockingDecision blocking_decision = ModuleBlockingDecision::kUnknown;
};
struct CacheUpdateResult {
base::MD5Digest old_md5_digest;
base::MD5Digest new_md5_digest;
};
using OnCacheUpdatedCallback =
base::RepeatingCallback<void(const CacheUpdateResult&)>;
// Creates an instance of the updater. The callback will be invoked every time
// the cache is updated.
// The parameters must outlive the lifetime of this class.
// The ModuleListFilter is taken by scoped_refptr since it will be used in a
// background sequence.
ModuleBlocklistCacheUpdater(
ModuleDatabaseEventSource* module_database_event_source,
const CertificateInfo& exe_certificate_info,
scoped_refptr<ModuleListFilter> module_list_filter,
const std::vector<third_party_dlls::PackedListModule>&
initial_blocklisted_modules,
OnCacheUpdatedCallback on_cache_updated_callback,
bool module_analysis_disabled);
ModuleBlocklistCacheUpdater(const ModuleBlocklistCacheUpdater&) = delete;
ModuleBlocklistCacheUpdater& operator=(const ModuleBlocklistCacheUpdater&) =
delete;
~ModuleBlocklistCacheUpdater() override;
// Returns true if the blocking of third-party modules is enabled. Can be
// called on any thread.
static bool IsBlockingEnabled();
// Returns the path to the module blocklist cache.
static base::FilePath GetModuleBlocklistCachePath();
// Deletes the module blocklist cache. This disables the blocking of third-
// party modules for the next browser launch.
static void DeleteModuleBlocklistCache();
// ModuleDatabaseObserver:
void OnNewModuleFound(const ModuleInfoKey& module_key,
const ModuleInfoData& module_data) override;
void OnKnownModuleLoaded(const ModuleInfoKey& module_key,
const ModuleInfoData& module_data) override;
void OnModuleDatabaseIdle() override;
// Returns the blocking decision for a module.
const ModuleBlockingState& GetModuleBlockingState(
const ModuleInfoKey& module_key) const;
// Disables the analysis of newly found modules. This is a one way switch that
// will apply until Chrome is restarted.
void DisableModuleAnalysis();
private:
// The state of the module with respect to the ModuleList.
enum class ModuleListState {
// The module is not in the module list at all.
kUnlisted,
// The module is in the module list and is explicitly allowlisted.
kAllowlisted,
// The module is in the module list and "blocklisted", but loading is
// tolerated.
kTolerated,
// The module is explicitly blocklisted.
kBlocklisted,
};
// Gets the state of a module with respect to the module list.
ModuleListState DetermineModuleListState(const ModuleInfoKey& module_key,
const ModuleInfoData& module_data);
// Determines whether or not a module *should* be allowlisted or blocklisted
// on the next startup. Returns a ModuleBlockingDecision.
ModuleBlockingDecision DetermineModuleBlockingDecision(
const ModuleInfoKey& module_key,
const ModuleInfoData& module_data);
// Posts the task to update the cache on |background_sequence_|.
void StartModuleBlocklistCacheUpdate();
// Invoked on the sequence that owns this instance when the cache is updated.
void OnModuleBlocklistCacheUpdated(const CacheUpdateResult& result);
const raw_ptr<ModuleDatabaseEventSource> module_database_event_source_;
const raw_ref<const CertificateInfo> exe_certificate_info_;
scoped_refptr<ModuleListFilter> module_list_filter_;
const raw_ref<const std::vector<third_party_dlls::PackedListModule>>
initial_blocklisted_modules_;
OnCacheUpdatedCallback on_cache_updated_callback_;
// The sequence on which the module blocklist cache file is updated.
scoped_refptr<base::SequencedTaskRunner> background_sequence_;
// Temporarily holds newly blocklisted modules before they are added to the
// module blocklist cache.
std::vector<third_party_dlls::PackedListModule> newly_blocklisted_modules_;
// Temporarily holds modules that were blocked from loading into the browser
// until they are used to update the cache.
std::vector<third_party_dlls::PackedListModule> blocked_modules_;
// Holds the blocking state for all known modules.
base::flat_map<ModuleInfoKey, ModuleBlockingState> module_blocking_states_;
// Indicates if the analysis of newly found modules is disabled. Used as a
// workaround for https://crbug.com/892294.
bool module_analysis_disabled_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<ModuleBlocklistCacheUpdater> weak_ptr_factory_;
};
#endif // CHROME_BROWSER_WIN_CONFLICTS_MODULE_BLOCKLIST_CACHE_UPDATER_H_
|