1 // Copyright (c) 2012 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/plugins/plugin_info_host_impl.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/memory/singleton.h"
15 #include "base/stl_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/task/post_task.h"
18 #include "base/task_runner_util.h"
19 #include "build/branding_buildflags.h"
20 #include "build/build_config.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
23 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
24 #include "chrome/browser/plugins/plugin_finder.h"
25 #include "chrome/browser/plugins/plugin_metadata.h"
26 #include "chrome/browser/plugins/plugin_prefs.h"
27 #include "chrome/browser/plugins/plugin_utils.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/browser_otr_state.h"
30 #include "chrome/common/buildflags.h"
31 #include "chrome/common/chrome_content_client.h"
32 #include "chrome/common/plugin.mojom.h"
33 #include "chrome/common/pref_names.h"
34 #include "components/component_updater/component_updater_service.h"
35 #include "components/content_settings/core/browser/content_settings_utils.h"
36 #include "components/content_settings/core/browser/host_content_settings_map.h"
37 #include "components/content_settings/core/common/content_settings.h"
38 #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
39 #include "components/nacl/common/buildflags.h"
40 #include "components/pref_registry/pref_registry_syncable.h"
41 #include "components/prefs/pref_service.h"
42 #include "components/ukm/content/source_url_recorder.h"
43 #include "content/public/browser/browser_task_traits.h"
44 #include "content/public/browser/plugin_service.h"
45 #include "content/public/browser/plugin_service_filter.h"
46 #include "content/public/browser/render_frame_host.h"
47 #include "content/public/common/content_constants.h"
48 #include "extensions/buildflags/buildflags.h"
49 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
50 #include "ppapi/buildflags/buildflags.h"
51 #include "services/metrics/public/cpp/ukm_builders.h"
52 #include "services/metrics/public/cpp/ukm_recorder.h"
53 #include "url/gurl.h"
54 #include "url/origin.h"
55 
56 #if BUILDFLAG(ENABLE_EXTENSIONS)
57 #include "components/guest_view/browser/guest_view_base.h"
58 #include "extensions/browser/extension_registry.h"
59 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
60 #include "extensions/common/constants.h"
61 #include "extensions/common/extension.h"
62 #include "extensions/common/manifest_handlers/webview_info.h"
63 #endif
64 
65 #if BUILDFLAG(ENABLE_NACL)
66 #include "components/nacl/common/nacl_constants.h"
67 #endif
68 
69 using content::PluginService;
70 using content::WebPluginInfo;
71 
72 namespace {
73 
74 class PluginInfoHostImplShutdownNotifierFactory
75     : public BrowserContextKeyedServiceShutdownNotifierFactory {
76  public:
GetInstance()77   static PluginInfoHostImplShutdownNotifierFactory* GetInstance() {
78     return base::Singleton<PluginInfoHostImplShutdownNotifierFactory>::get();
79   }
80 
81  private:
82   friend struct base::DefaultSingletonTraits<
83       PluginInfoHostImplShutdownNotifierFactory>;
84 
PluginInfoHostImplShutdownNotifierFactory()85   PluginInfoHostImplShutdownNotifierFactory()
86       : BrowserContextKeyedServiceShutdownNotifierFactory(
87             "PluginInfoHostImpl") {}
88 
~PluginInfoHostImplShutdownNotifierFactory()89   ~PluginInfoHostImplShutdownNotifierFactory() override {}
90 
91   DISALLOW_COPY_AND_ASSIGN(PluginInfoHostImplShutdownNotifierFactory);
92 };
93 
94 #if BUILDFLAG(ENABLE_EXTENSIONS)
95 // Returns whether a request from a plugin to load |resource| from a renderer
96 // with process id |process_id| is a request for an internal resource by an app
97 // listed in |accessible_resources| in its manifest.
IsPluginLoadingAccessibleResourceInWebView(extensions::ExtensionRegistry * extension_registry,int process_id,const GURL & resource)98 bool IsPluginLoadingAccessibleResourceInWebView(
99     extensions::ExtensionRegistry* extension_registry,
100     int process_id,
101     const GURL& resource) {
102   extensions::WebViewRendererState* renderer_state =
103       extensions::WebViewRendererState::GetInstance();
104   std::string partition_id;
105   if (!renderer_state->IsGuest(process_id) ||
106       !renderer_state->GetPartitionID(process_id, &partition_id)) {
107     return false;
108   }
109 
110   const std::string extension_id = resource.host();
111   const extensions::Extension* extension = extension_registry->GetExtensionById(
112       extension_id, extensions::ExtensionRegistry::ENABLED);
113   if (!extension || !extensions::WebviewInfo::IsResourceWebviewAccessible(
114                         extension, partition_id, resource.path())) {
115     return false;
116   }
117 
118   // Make sure the renderer making the request actually belongs to the
119   // same extension.
120   std::string owner_extension;
121   return renderer_state->GetOwnerInfo(process_id, nullptr, &owner_extension) &&
122          owner_extension == extension_id;
123 }
124 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
125 
126 }  // namespace
127 
Context(int render_process_id,Profile * profile)128 PluginInfoHostImpl::Context::Context(int render_process_id, Profile* profile)
129     : render_process_id_(render_process_id),
130 #if BUILDFLAG(ENABLE_EXTENSIONS)
131       extension_registry_(extensions::ExtensionRegistry::Get(profile)),
132 #endif
133       host_content_settings_map_(
134           HostContentSettingsMapFactory::GetForProfile(profile)),
135       plugin_prefs_(PluginPrefs::GetForProfile(profile)) {
136   allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated,
137                                profile->GetPrefs());
138   run_all_flash_in_allow_mode_.Init(prefs::kRunAllFlashInAllowMode,
139                                     profile->GetPrefs());
140 }
141 
~Context()142 PluginInfoHostImpl::Context::~Context() {}
143 
ShutdownOnUIThread()144 void PluginInfoHostImpl::Context::ShutdownOnUIThread() {
145   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
146   allow_outdated_plugins_.Destroy();
147   run_all_flash_in_allow_mode_.Destroy();
148 }
149 
PluginInfoHostImpl(int render_process_id,Profile * profile)150 PluginInfoHostImpl::PluginInfoHostImpl(int render_process_id, Profile* profile)
151     : context_(render_process_id, profile) {
152   shutdown_notifier_ =
153       PluginInfoHostImplShutdownNotifierFactory::GetInstance()
154           ->Get(profile)
155           ->Subscribe(base::Bind(&PluginInfoHostImpl::ShutdownOnUIThread,
156                                  base::Unretained(this)));
157 }
158 
ShutdownOnUIThread()159 void PluginInfoHostImpl::ShutdownOnUIThread() {
160   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
161   context_.ShutdownOnUIThread();
162   shutdown_notifier_.reset();
163 }
164 
165 // static
RegisterUserPrefs(user_prefs::PrefRegistrySyncable * registry)166 void PluginInfoHostImpl::RegisterUserPrefs(
167     user_prefs::PrefRegistrySyncable* registry) {
168   registry->RegisterBooleanPref(prefs::kPluginsAllowOutdated, false);
169   registry->RegisterBooleanPref(prefs::kRunAllFlashInAllowMode, false);
170 }
171 
~PluginInfoHostImpl()172 PluginInfoHostImpl::~PluginInfoHostImpl() {}
173 
174 struct PluginInfoHostImpl::GetPluginInfo_Params {
175   int render_frame_id;
176   GURL url;
177   url::Origin main_frame_origin;
178   std::string mime_type;
179 };
180 
GetPluginInfo(int32_t render_frame_id,const GURL & url,const url::Origin & origin,const std::string & mime_type,GetPluginInfoCallback callback)181 void PluginInfoHostImpl::GetPluginInfo(int32_t render_frame_id,
182                                        const GURL& url,
183                                        const url::Origin& origin,
184                                        const std::string& mime_type,
185                                        GetPluginInfoCallback callback) {
186   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
187   GetPluginInfo_Params params = {render_frame_id, url, origin, mime_type};
188   PluginService::GetInstance()->GetPlugins(
189       base::BindOnce(&PluginInfoHostImpl::PluginsLoaded,
190                      weak_factory_.GetWeakPtr(), params, std::move(callback)));
191 }
192 
PluginsLoaded(const GetPluginInfo_Params & params,GetPluginInfoCallback callback,const std::vector<WebPluginInfo> & plugins)193 void PluginInfoHostImpl::PluginsLoaded(
194     const GetPluginInfo_Params& params,
195     GetPluginInfoCallback callback,
196     const std::vector<WebPluginInfo>& plugins) {
197   chrome::mojom::PluginInfoPtr output = chrome::mojom::PluginInfo::New();
198   // This also fills in |actual_mime_type|.
199   std::unique_ptr<PluginMetadata> plugin_metadata;
200   if (context_.FindEnabledPlugin(params.render_frame_id, params.url,
201                                  params.main_frame_origin, params.mime_type,
202                                  &output->status, &output->plugin,
203                                  &output->actual_mime_type, &plugin_metadata)) {
204     context_.DecidePluginStatus(
205         params.url, params.main_frame_origin, output->plugin,
206         plugin_metadata->GetSecurityStatus(output->plugin),
207         plugin_metadata->identifier(), &output->status);
208   }
209 
210   if (output->status == chrome::mojom::PluginStatus::kNotFound) {
211     // Check to see if the component updater can fetch an implementation.
212     std::unique_ptr<component_updater::ComponentInfo> cus_plugin_info =
213         g_browser_process->component_updater()->GetComponentForMimeType(
214             params.mime_type);
215     ComponentPluginLookupDone(params, std::move(output), std::move(callback),
216                               std::move(plugin_metadata),
217                               std::move(cus_plugin_info));
218   } else {
219     GetPluginInfoFinish(params, std::move(output), std::move(callback),
220                         std::move(plugin_metadata));
221   }
222 }
223 
DecidePluginStatus(const GURL & url,const url::Origin & main_frame_origin,const WebPluginInfo & plugin,PluginMetadata::SecurityStatus security_status,const std::string & plugin_identifier,chrome::mojom::PluginStatus * status) const224 void PluginInfoHostImpl::Context::DecidePluginStatus(
225     const GURL& url,
226     const url::Origin& main_frame_origin,
227     const WebPluginInfo& plugin,
228     PluginMetadata::SecurityStatus security_status,
229     const std::string& plugin_identifier,
230     chrome::mojom::PluginStatus* status) const {
231   if (security_status == PluginMetadata::SECURITY_STATUS_FULLY_TRUSTED) {
232     *status = chrome::mojom::PluginStatus::kAllowed;
233     return;
234   }
235 
236 // This block is separate from the outdated check, because the deprecated UI
237 // must take precedence over any content setting or HTML5 by Default.
238 #if BUILDFLAG(ENABLE_PLUGINS)
239   if (security_status == PluginMetadata::SECURITY_STATUS_DEPRECATED) {
240     *status = chrome::mojom::PluginStatus::kDeprecated;
241     return;
242   }
243 #endif
244 
245   ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT;
246   bool uses_default_content_setting = true;
247   bool is_managed = false;
248   // Check plugin content settings. The primary URL is the top origin URL and
249   // the secondary URL is the plugin URL.
250   PluginUtils::GetPluginContentSetting(
251       host_content_settings_map_, plugin, main_frame_origin, url,
252       plugin_identifier, &plugin_setting, &uses_default_content_setting,
253       &is_managed);
254 
255   DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT);
256   DCHECK(plugin_setting != CONTENT_SETTING_ASK);
257 
258   if (*status == chrome::mojom::PluginStatus::kFlashHiddenPreferHtml) {
259     if (plugin_setting == CONTENT_SETTING_BLOCK) {
260       *status = is_managed ? chrome::mojom::PluginStatus::kBlockedByPolicy
261                            : chrome::mojom::PluginStatus::kBlockedNoLoading;
262     }
263     return;
264   }
265 
266 #if BUILDFLAG(ENABLE_PLUGINS)
267   // Check if the plugin is outdated.
268   if (security_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE &&
269       !allow_outdated_plugins_.GetValue()) {
270     if (allow_outdated_plugins_.IsManaged()) {
271       *status = chrome::mojom::PluginStatus::kOutdatedDisallowed;
272     } else {
273       *status = chrome::mojom::PluginStatus::kOutdatedBlocked;
274     }
275     return;
276   }
277 #endif  // BUILDFLAG(ENABLE_PLUGINS)
278 
279   // Check if the plugin is crashing too much.
280   if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) &&
281       plugin_setting != CONTENT_SETTING_BLOCK && uses_default_content_setting) {
282     *status = chrome::mojom::PluginStatus::kUnauthorized;
283     return;
284   }
285 
286 #if BUILDFLAG(ENABLE_EXTENSIONS)
287   // If an app has explicitly made internal resources available by listing them
288   // in |accessible_resources| in the manifest, then allow them to be loaded by
289   // plugins inside a guest-view.
290   if (url.SchemeIs(extensions::kExtensionScheme) && !is_managed &&
291       plugin_setting == CONTENT_SETTING_BLOCK &&
292       IsPluginLoadingAccessibleResourceInWebView(extension_registry_,
293                                                  render_process_id_, url)) {
294     plugin_setting = CONTENT_SETTING_ALLOW;
295   }
296 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
297 
298   if (plugin_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT ||
299       (plugin_setting == CONTENT_SETTING_ALLOW &&
300        !run_all_flash_in_allow_mode_.GetValue())) {
301     *status = chrome::mojom::PluginStatus::kPlayImportantContent;
302   } else if (plugin_setting == CONTENT_SETTING_BLOCK) {
303     *status = is_managed ? chrome::mojom::PluginStatus::kBlockedByPolicy
304                          : chrome::mojom::PluginStatus::kBlocked;
305   }
306 
307 #if BUILDFLAG(ENABLE_EXTENSIONS)
308   // Allow an embedder of <webview> to block a plugin from being loaded inside
309   // the guest. In order to do this, set the status to 'Unauthorized' here,
310   // and update the status as appropriate depending on the response from the
311   // embedder.
312   if (*status == chrome::mojom::PluginStatus::kAllowed ||
313       *status == chrome::mojom::PluginStatus::kBlocked ||
314       *status == chrome::mojom::PluginStatus::kPlayImportantContent) {
315     if (extensions::WebViewRendererState::GetInstance()->IsGuest(
316             render_process_id_))
317       *status = chrome::mojom::PluginStatus::kUnauthorized;
318   }
319 #endif
320 }
321 
FindEnabledPlugin(int render_frame_id,const GURL & url,const url::Origin & main_frame_origin,const std::string & mime_type,chrome::mojom::PluginStatus * status,WebPluginInfo * plugin,std::string * actual_mime_type,std::unique_ptr<PluginMetadata> * plugin_metadata) const322 bool PluginInfoHostImpl::Context::FindEnabledPlugin(
323     int render_frame_id,
324     const GURL& url,
325     const url::Origin& main_frame_origin,
326     const std::string& mime_type,
327     chrome::mojom::PluginStatus* status,
328     WebPluginInfo* plugin,
329     std::string* actual_mime_type,
330     std::unique_ptr<PluginMetadata>* plugin_metadata) const {
331   *status = chrome::mojom::PluginStatus::kAllowed;
332 
333   bool allow_wildcard = true;
334   std::vector<WebPluginInfo> matching_plugins;
335   std::vector<std::string> mime_types;
336   PluginService::GetInstance()->GetPluginInfoArray(
337       url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
338 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
339   base::EraseIf(matching_plugins, [&](const WebPluginInfo& info) {
340     return info.path.value() == ChromeContentClient::kNotPresent;
341   });
342 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
343   if (matching_plugins.empty()) {
344     *status = chrome::mojom::PluginStatus::kNotFound;
345     return false;
346   }
347 
348   content::PluginServiceFilter* filter =
349       PluginService::GetInstance()->GetFilter();
350   size_t i = 0;
351   for (; i < matching_plugins.size(); ++i) {
352     if (!filter ||
353         filter->IsPluginAvailable(render_process_id_, render_frame_id, url,
354                                   main_frame_origin, &matching_plugins[i])) {
355       break;
356     }
357   }
358 
359   // If we broke out of the loop, we have found an enabled plugin.
360   bool enabled = i < matching_plugins.size();
361   if (!enabled) {
362     // Otherwise, we only found disabled plugins, so we take the first one.
363     i = 0;
364     *status = chrome::mojom::PluginStatus::kDisabled;
365   }
366 
367   *plugin = matching_plugins[i];
368   *actual_mime_type = mime_types[i];
369   if (plugin_metadata)
370     *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
371 
372   return enabled;
373 }
374 
ComponentPluginLookupDone(const GetPluginInfo_Params & params,chrome::mojom::PluginInfoPtr output,GetPluginInfoCallback callback,std::unique_ptr<PluginMetadata> plugin_metadata,std::unique_ptr<component_updater::ComponentInfo> cus_plugin_info)375 void PluginInfoHostImpl::ComponentPluginLookupDone(
376     const GetPluginInfo_Params& params,
377     chrome::mojom::PluginInfoPtr output,
378     GetPluginInfoCallback callback,
379     std::unique_ptr<PluginMetadata> plugin_metadata,
380     std::unique_ptr<component_updater::ComponentInfo> cus_plugin_info) {
381   if (cus_plugin_info) {
382     output->status = chrome::mojom::PluginStatus::kComponentUpdateRequired;
383 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
384     if (cus_plugin_info->version != base::Version("0")) {
385       output->status = chrome::mojom::PluginStatus::kRestartRequired;
386     }
387 #endif
388     // Component Updater wouldn't provide a deprecated plugin.
389     bool plugin_is_deprecated = false;
390     plugin_metadata = std::make_unique<PluginMetadata>(
391         cus_plugin_info->id, cus_plugin_info->name, false, GURL(), GURL(),
392         base::ASCIIToUTF16(cus_plugin_info->id), std::string(),
393         plugin_is_deprecated);
394   }
395   GetPluginInfoFinish(params, std::move(output), std::move(callback),
396                       std::move(plugin_metadata));
397 }
398 
GetPluginInfoFinish(const GetPluginInfo_Params & params,chrome::mojom::PluginInfoPtr output,GetPluginInfoCallback callback,std::unique_ptr<PluginMetadata> plugin_metadata)399 void PluginInfoHostImpl::GetPluginInfoFinish(
400     const GetPluginInfo_Params& params,
401     chrome::mojom::PluginInfoPtr output,
402     GetPluginInfoCallback callback,
403     std::unique_ptr<PluginMetadata> plugin_metadata) {
404   if (plugin_metadata) {
405     output->group_identifier = plugin_metadata->identifier();
406     output->group_name = plugin_metadata->name();
407   }
408 
409   context_.MaybeGrantAccess(output->status, output->plugin.path);
410 
411   std::move(callback).Run(std::move(output));
412 }
413 
MaybeGrantAccess(chrome::mojom::PluginStatus status,const base::FilePath & path) const414 void PluginInfoHostImpl::Context::MaybeGrantAccess(
415     chrome::mojom::PluginStatus status,
416     const base::FilePath& path) const {
417   if (status == chrome::mojom::PluginStatus::kAllowed ||
418       status == chrome::mojom::PluginStatus::kPlayImportantContent) {
419     ChromePluginServiceFilter::GetInstance()->AuthorizePlugin(
420         render_process_id_, path);
421   }
422 }
423 
IsPluginEnabled(const content::WebPluginInfo & plugin) const424 bool PluginInfoHostImpl::Context::IsPluginEnabled(
425     const content::WebPluginInfo& plugin) const {
426   return plugin_prefs_->IsPluginEnabled(plugin);
427 }
428