1 // Copyright 2018 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 #include "chrome/browser/extensions/forced_extensions/force_installed_tracker.h"
6 
7 #include "base/bind.h"
8 #include "base/values.h"
9 #include "chrome/browser/extensions/extension_management_constants.h"
10 #include "chrome/browser/extensions/external_provider_impl.h"
11 #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h"
12 #include "chrome/browser/policy/profile_policy_connector.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "components/prefs/pref_service.h"
15 #include "extensions/browser/install/crx_install_error.h"
16 #include "extensions/browser/pref_names.h"
17 #include "extensions/common/extension_urls.h"
18 
19 #if defined(OS_CHROMEOS)
20 #include "components/arc/arc_prefs.h"
21 #endif  // defined(OS_CHROMEOS)
22 
23 namespace extensions {
24 
ForceInstalledTracker(ExtensionRegistry * registry,Profile * profile)25 ForceInstalledTracker::ForceInstalledTracker(ExtensionRegistry* registry,
26                                              Profile* profile)
27     : extension_management_(
28           ExtensionManagementFactory::GetForBrowserContext(profile)),
29       registry_(registry),
30       profile_(profile),
31       pref_service_(profile->GetPrefs()) {
32   // Load immediately if PolicyService is ready, or wait for it to finish
33   // initializing first.
34   if (policy_service()->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME))
35     OnForcedExtensionsPrefReady();
36   else
37     policy_service()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
38 }
39 
~ForceInstalledTracker()40 ForceInstalledTracker::~ForceInstalledTracker() {
41   policy_service()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
42 }
43 
UpdateCounters(ExtensionStatus status,int delta)44 void ForceInstalledTracker::UpdateCounters(ExtensionStatus status, int delta) {
45   switch (status) {
46     case ExtensionStatus::PENDING:
47       load_pending_count_ += delta;
48       FALLTHROUGH;
49     case ExtensionStatus::LOADED:
50       ready_pending_count_ += delta;
51       break;
52     case ExtensionStatus::READY:
53     case ExtensionStatus::FAILED:
54       break;
55   }
56 }
57 
AddExtensionInfo(const ExtensionId & extension_id,ExtensionStatus status,bool is_from_store)58 void ForceInstalledTracker::AddExtensionInfo(const ExtensionId& extension_id,
59                                              ExtensionStatus status,
60                                              bool is_from_store) {
61   auto result =
62       extensions_.emplace(extension_id, ExtensionInfo{status, is_from_store});
63   DCHECK(result.second);
64   UpdateCounters(result.first->second.status, +1);
65 }
66 
ChangeExtensionStatus(const ExtensionId & extension_id,ExtensionStatus status)67 void ForceInstalledTracker::ChangeExtensionStatus(
68     const ExtensionId& extension_id,
69     ExtensionStatus status) {
70   DCHECK_GE(status_, kWaitingForExtensionLoads);
71   auto item = extensions_.find(extension_id);
72   if (item == extensions_.end())
73     return;
74   UpdateCounters(item->second.status, -1);
75   item->second.status = status;
76   UpdateCounters(item->second.status, +1);
77 }
78 
OnPolicyUpdated(const policy::PolicyNamespace & ns,const policy::PolicyMap & previous,const policy::PolicyMap & current)79 void ForceInstalledTracker::OnPolicyUpdated(const policy::PolicyNamespace& ns,
80                                             const policy::PolicyMap& previous,
81                                             const policy::PolicyMap& current) {}
82 
OnPolicyServiceInitialized(policy::PolicyDomain domain)83 void ForceInstalledTracker::OnPolicyServiceInitialized(
84     policy::PolicyDomain domain) {
85   DCHECK_EQ(domain, policy::POLICY_DOMAIN_CHROME);
86   DCHECK_EQ(status_, kWaitingForPolicyService);
87   policy_service()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
88   OnForcedExtensionsPrefReady();
89 }
90 
OnForcedExtensionsPrefReady()91 void ForceInstalledTracker::OnForcedExtensionsPrefReady() {
92   DCHECK(
93       policy_service()->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME));
94   DCHECK_EQ(status_, kWaitingForPolicyService);
95 
96   // Listen for extension loads and install failures.
97   status_ = kWaitingForExtensionLoads;
98   registry_observer_.Add(registry_);
99   collector_observer_.Add(InstallStageTracker::Get(profile_));
100 
101   const base::DictionaryValue* value =
102       pref_service_->GetDictionary(pref_names::kInstallForceList);
103   if (value) {
104     // Add each extension to |extensions_|.
105     for (const auto& entry : *value) {
106       const ExtensionId& extension_id = entry.first;
107       std::string* update_url = nullptr;
108       if (entry.second->is_dict()) {
109         update_url = entry.second->FindStringKey(
110             ExternalProviderImpl::kExternalUpdateUrl);
111       }
112       bool is_from_store =
113           update_url && *update_url == extension_urls::kChromeWebstoreUpdateURL;
114 
115       ExtensionStatus status = ExtensionStatus::PENDING;
116       if (registry_->enabled_extensions().Contains(extension_id)) {
117         status = registry_->ready_extensions().Contains(extension_id)
118                      ? ExtensionStatus::READY
119                      : ExtensionStatus::LOADED;
120       }
121       AddExtensionInfo(extension_id, status, is_from_store);
122     }
123   }
124 
125   // Run observers if there are no pending installs.
126   MaybeNotifyObservers();
127 }
128 
OnShutdown(ExtensionRegistry *)129 void ForceInstalledTracker::OnShutdown(ExtensionRegistry*) {
130   registry_observer_.RemoveAll();
131 }
132 
AddObserver(Observer * obs)133 void ForceInstalledTracker::AddObserver(Observer* obs) {
134   observers_.AddObserver(obs);
135 }
136 
RemoveObserver(Observer * obs)137 void ForceInstalledTracker::RemoveObserver(Observer* obs) {
138   observers_.RemoveObserver(obs);
139 }
140 
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)141 void ForceInstalledTracker::OnExtensionLoaded(
142     content::BrowserContext* browser_context,
143     const Extension* extension) {
144   ChangeExtensionStatus(extension->id(), ExtensionStatus::LOADED);
145   MaybeNotifyObservers();
146 }
147 
OnExtensionReady(content::BrowserContext * browser_context,const Extension * extension)148 void ForceInstalledTracker::OnExtensionReady(
149     content::BrowserContext* browser_context,
150     const Extension* extension) {
151   ChangeExtensionStatus(extension->id(), ExtensionStatus::READY);
152   MaybeNotifyObservers();
153 }
154 
OnExtensionInstallationFailed(const ExtensionId & extension_id,InstallStageTracker::FailureReason reason)155 void ForceInstalledTracker::OnExtensionInstallationFailed(
156     const ExtensionId& extension_id,
157     InstallStageTracker::FailureReason reason) {
158   auto item = extensions_.find(extension_id);
159   // If the extension is loaded, ignore the failure.
160   if (item == extensions_.end() ||
161       item->second.status == ExtensionStatus::LOADED ||
162       item->second.status == ExtensionStatus::READY)
163     return;
164   ChangeExtensionStatus(extension_id, ExtensionStatus::FAILED);
165   MaybeNotifyObservers();
166 }
167 
IsDoneLoading() const168 bool ForceInstalledTracker::IsDoneLoading() const {
169   return status_ == kWaitingForExtensionReady || status_ == kComplete;
170 }
171 
OnExtensionDownloadCacheStatusRetrieved(const ExtensionId & id,ExtensionDownloaderDelegate::CacheStatus cache_status)172 void ForceInstalledTracker::OnExtensionDownloadCacheStatusRetrieved(
173     const ExtensionId& id,
174     ExtensionDownloaderDelegate::CacheStatus cache_status) {
175   // Report cache status only for forced installed extension.
176   if (extensions_.find(id) != extensions_.end()) {
177     for (auto& obs : observers_)
178       obs.OnExtensionDownloadCacheStatusRetrieved(id, cache_status);
179   }
180 }
181 
IsReady() const182 bool ForceInstalledTracker::IsReady() const {
183   return status_ == kComplete;
184 }
185 
IsMisconfiguration(const InstallStageTracker::InstallationData & installation_data,const ExtensionId & id) const186 bool ForceInstalledTracker::IsMisconfiguration(
187     const InstallStageTracker::InstallationData& installation_data,
188     const ExtensionId& id) const {
189   if (installation_data.install_error_detail) {
190     CrxInstallErrorDetail detail =
191         installation_data.install_error_detail.value();
192     if (detail == CrxInstallErrorDetail::KIOSK_MODE_ONLY)
193       return true;
194 
195     if (installation_data.extension_type &&
196         detail == CrxInstallErrorDetail::DISALLOWED_BY_POLICY &&
197         !extension_management_->IsAllowedManifestType(
198             installation_data.extension_type.value(), id)) {
199       return true;
200     }
201   }
202 
203 #if defined(OS_CHROMEOS)
204   // REPLACED_BY_ARC_APP error is a misconfiguration if ARC++ is enabled for
205   // the device.
206   if (profile_->GetPrefs()->IsManagedPreference(arc::prefs::kArcEnabled) &&
207       profile_->GetPrefs()->GetBoolean(arc::prefs::kArcEnabled) &&
208       installation_data.failure_reason ==
209           InstallStageTracker::FailureReason::REPLACED_BY_ARC_APP) {
210     return true;
211   }
212 #endif  // defined(OS_CHROMEOS)
213 
214   if (installation_data.failure_reason ==
215       InstallStageTracker::FailureReason::NOT_PERFORMING_NEW_INSTALL) {
216     return true;
217   }
218   if (installation_data.failure_reason ==
219       InstallStageTracker::FailureReason::CRX_FETCH_URL_EMPTY) {
220     DCHECK(installation_data.no_updates_info);
221     if (installation_data.no_updates_info.value() ==
222         InstallStageTracker::NoUpdatesInfo::kEmpty) {
223       return true;
224     }
225   }
226 
227   if (installation_data.manifest_invalid_error ==
228           ManifestInvalidError::BAD_APP_STATUS &&
229       installation_data.app_status_error ==
230           InstallStageTracker::AppStatusError::kErrorUnknownApplication) {
231     return true;
232   }
233 
234   if (installation_data.unpacker_failure_reason ==
235       SandboxedUnpackerFailureReason::CRX_HEADER_INVALID) {
236     auto extension = extensions_.find(id);
237     // Extension id may be missing from this list if there is a change in
238     // ExtensionInstallForcelist policy after the user has logged in and
239     // |IsMisconfiguration| method is called from
240     // |ExtensionInstallEventLogCollector|.
241     if (extension != extensions_.end() && !extension->second.is_from_store &&
242         !IsExtensionFetchedFromCache(
243             installation_data.downloading_cache_status)) {
244       return true;
245     }
246   }
247 
248   return false;
249 }
250 
251 // static
IsExtensionFetchedFromCache(const base::Optional<ExtensionDownloaderDelegate::CacheStatus> & status)252 bool ForceInstalledTracker::IsExtensionFetchedFromCache(
253     const base::Optional<ExtensionDownloaderDelegate::CacheStatus>& status) {
254   if (!status)
255     return false;
256   return status.value() == ExtensionDownloaderDelegate::CacheStatus::
257                                CACHE_HIT_ON_MANIFEST_FETCH_FAILURE ||
258          status.value() == ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT;
259 }
260 
policy_service()261 policy::PolicyService* ForceInstalledTracker::policy_service() {
262   return profile_->GetProfilePolicyConnector()->policy_service();
263 }
264 
MaybeNotifyObservers()265 void ForceInstalledTracker::MaybeNotifyObservers() {
266   DCHECK_GE(status_, kWaitingForExtensionLoads);
267   if (status_ == kWaitingForExtensionLoads && load_pending_count_ == 0) {
268     for (auto& obs : observers_)
269       obs.OnForceInstalledExtensionsLoaded();
270     status_ = kWaitingForExtensionReady;
271   }
272   if (status_ == kWaitingForExtensionReady && ready_pending_count_ == 0) {
273     for (auto& obs : observers_)
274       obs.OnForceInstalledExtensionsReady();
275     status_ = kComplete;
276     registry_observer_.RemoveAll();
277     collector_observer_.RemoveAll();
278     InstallStageTracker::Get(profile_)->Clear();
279   }
280 }
281 
282 }  //  namespace extensions
283