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.
5 #include "chrome/browser/plugins/plugin_info_message_filter.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/metrics/histogram.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/content_settings/content_settings_utils.h"
13 #include "chrome/browser/content_settings/host_content_settings_map.h"
14 #include "chrome/browser/extensions/extension_renderer_state.h"
15 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
16 #include "chrome/browser/plugins/plugin_finder.h"
17 #include "chrome/browser/plugins/plugin_metadata.h"
18 #include "chrome/browser/plugins/plugin_prefs.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/chrome_content_client.h"
21 #include "chrome/common/content_settings.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/render_messages.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/plugin_service.h"
26 #include "content/public/browser/plugin_service_filter.h"
29 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
32 #include "base/win/metro.h"
35 using content::PluginService;
36 using content::WebPluginInfo;
40 // For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
41 bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
42 if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
43 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) {
47 // Treat Native Client invocations like JavaScript.
48 if (plugin.name == base::ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
51 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
52 // Treat CDM invocations like JavaScript.
53 if (plugin.name == base::ASCIIToUTF16(kWidevineCdmDisplayName)) {
54 DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
57 #endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
64 PluginInfoMessageFilter::Context::Context(int render_process_id,
66 : render_process_id_(render_process_id),
67 resource_context_(profile->GetResourceContext()),
68 host_content_settings_map_(profile->GetHostContentSettingsMap()),
69 plugin_prefs_(PluginPrefs::GetForProfile(profile)) {
70 allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated,
72 allow_outdated_plugins_.MoveToThread(
73 content::BrowserThread::GetMessageLoopProxyForThread(
74 content::BrowserThread::IO));
75 always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize,
77 always_authorize_plugins_.MoveToThread(
78 content::BrowserThread::GetMessageLoopProxyForThread(
79 content::BrowserThread::IO));
82 PluginInfoMessageFilter::Context::~Context() {
85 PluginInfoMessageFilter::PluginInfoMessageFilter(
86 int render_process_id,
88 : BrowserMessageFilter(ChromeMsgStart),
89 context_(render_process_id, profile),
90 weak_ptr_factory_(this) {
93 bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) {
94 IPC_BEGIN_MESSAGE_MAP(PluginInfoMessageFilter, message)
95 IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo,
98 ChromeViewHostMsg_IsInternalPluginRegisteredForMimeType,
99 OnIsInternalPluginRegisteredForMimeType)
100 IPC_MESSAGE_UNHANDLED(return false)
101 IPC_END_MESSAGE_MAP()
105 void PluginInfoMessageFilter::OnDestruct() const {
106 const_cast<PluginInfoMessageFilter*>(this)->
107 weak_ptr_factory_.InvalidateWeakPtrs();
109 // Destroy on the UI thread because we contain a |PrefMember|.
110 content::BrowserThread::DeleteOnUIThread::Destruct(this);
113 PluginInfoMessageFilter::~PluginInfoMessageFilter() {}
115 struct PluginInfoMessageFilter::GetPluginInfo_Params {
119 std::string mime_type;
122 void PluginInfoMessageFilter::OnGetPluginInfo(
125 const GURL& top_origin_url,
126 const std::string& mime_type,
127 IPC::Message* reply_msg) {
128 GetPluginInfo_Params params = {
134 PluginService::GetInstance()->GetPlugins(
135 base::Bind(&PluginInfoMessageFilter::PluginsLoaded,
136 weak_ptr_factory_.GetWeakPtr(),
140 void PluginInfoMessageFilter::PluginsLoaded(
141 const GetPluginInfo_Params& params,
142 IPC::Message* reply_msg,
143 const std::vector<WebPluginInfo>& plugins) {
144 ChromeViewHostMsg_GetPluginInfo_Output output;
145 // This also fills in |actual_mime_type|.
146 scoped_ptr<PluginMetadata> plugin_metadata;
147 if (context_.FindEnabledPlugin(params.render_frame_id, params.url,
148 params.top_origin_url, params.mime_type,
149 &output.status, &output.plugin,
150 &output.actual_mime_type,
152 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(),
156 if (plugin_metadata) {
157 output.group_identifier = plugin_metadata->identifier();
158 output.group_name = plugin_metadata->name();
161 context_.MaybeGrantAccess(output.status, output.plugin.path);
163 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
167 void PluginInfoMessageFilter::OnIsInternalPluginRegisteredForMimeType(
168 const std::string& mime_type,
170 std::vector<base::string16>* additional_param_names,
171 std::vector<base::string16>* additional_param_values) {
172 std::vector<WebPluginInfo> plugins;
173 PluginService::GetInstance()->GetInternalPlugins(&plugins);
174 for (size_t i = 0; i < plugins.size(); ++i) {
175 const std::vector<content::WebPluginMimeType>& mime_types =
176 plugins[i].mime_types;
177 for (size_t j = 0; j < mime_types.size(); ++j) {
178 if (mime_types[j].mime_type == mime_type) {
179 *is_registered = true;
180 *additional_param_names = mime_types[j].additional_param_names;
181 *additional_param_values = mime_types[j].additional_param_values;
187 *is_registered = false;
190 void PluginInfoMessageFilter::Context::DecidePluginStatus(
191 const GetPluginInfo_Params& params,
192 const WebPluginInfo& plugin,
193 const PluginMetadata* plugin_metadata,
194 ChromeViewHostMsg_GetPluginInfo_Status* status) const {
196 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
197 base::win::IsMetroProcess()) {
199 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
203 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
204 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
205 // NPAPI plugins are not supported inside <webview> guests.
206 if (ExtensionRendererState::GetInstance()->IsWebViewRenderer(
207 render_process_id_)) {
209 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
214 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT;
215 bool uses_default_content_setting = true;
216 bool is_managed = false;
217 // Check plug-in content settings. The primary URL is the top origin URL and
218 // the secondary URL is the plug-in URL.
219 GetPluginContentSetting(plugin, params.top_origin_url, params.url,
220 plugin_metadata->identifier(), &plugin_setting,
221 &uses_default_content_setting, &is_managed);
222 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT);
224 PluginMetadata::SecurityStatus plugin_status =
225 plugin_metadata->GetSecurityStatus(plugin);
226 #if defined(ENABLE_PLUGIN_INSTALLATION)
227 // Check if the plug-in is outdated.
228 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE &&
229 !allow_outdated_plugins_.GetValue()) {
230 if (allow_outdated_plugins_.IsManaged()) {
232 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
234 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
239 // Check if the plug-in or its group is enabled by policy.
240 PluginPrefs::PolicyStatus plugin_policy =
241 plugin_prefs_->PolicyStatusForPlugin(plugin.name);
242 PluginPrefs::PolicyStatus group_policy =
243 plugin_prefs_->PolicyStatusForPlugin(plugin_metadata->name());
245 // Check if the plug-in requires authorization.
247 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION &&
248 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
249 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS &&
250 !always_authorize_plugins_.GetValue() &&
251 plugin_setting != CONTENT_SETTING_BLOCK &&
252 uses_default_content_setting &&
253 plugin_policy != PluginPrefs::POLICY_ENABLED &&
254 group_policy != PluginPrefs::POLICY_ENABLED &&
255 !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted(
257 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
261 // Check if the plug-in is crashing too much.
262 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) &&
263 !always_authorize_plugins_.GetValue() &&
264 plugin_setting != CONTENT_SETTING_BLOCK &&
265 uses_default_content_setting) {
266 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
270 if (plugin_setting == CONTENT_SETTING_ASK) {
271 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay;
272 } else if (plugin_setting == CONTENT_SETTING_BLOCK) {
274 is_managed ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy
275 : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
278 if (status->value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed) {
279 // Allow an embedder of <webview> to block a plugin from being loaded inside
280 // the guest. In order to do this, set the status to 'Unauthorized' here,
281 // and update the status as appropriate depending on the response from the
283 if (ExtensionRendererState::GetInstance()->IsWebViewRenderer(
284 render_process_id_)) {
285 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
290 bool PluginInfoMessageFilter::Context::FindEnabledPlugin(
293 const GURL& top_origin_url,
294 const std::string& mime_type,
295 ChromeViewHostMsg_GetPluginInfo_Status* status,
296 WebPluginInfo* plugin,
297 std::string* actual_mime_type,
298 scoped_ptr<PluginMetadata>* plugin_metadata) const {
299 bool allow_wildcard = true;
300 std::vector<WebPluginInfo> matching_plugins;
301 std::vector<std::string> mime_types;
302 PluginService::GetInstance()->GetPluginInfoArray(
303 url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
304 if (matching_plugins.empty()) {
305 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound;
309 content::PluginServiceFilter* filter =
310 PluginService::GetInstance()->GetFilter();
312 for (; i < matching_plugins.size(); ++i) {
313 if (!filter || filter->IsPluginAvailable(render_process_id_,
318 &matching_plugins[i])) {
323 // If we broke out of the loop, we have found an enabled plug-in.
324 bool enabled = i < matching_plugins.size();
326 // Otherwise, we only found disabled plug-ins, so we take the first one.
328 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled;
331 *plugin = matching_plugins[i];
332 *actual_mime_type = mime_types[i];
334 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
339 void PluginInfoMessageFilter::Context::GetPluginContentSetting(
340 const WebPluginInfo& plugin,
341 const GURL& policy_url,
342 const GURL& plugin_url,
343 const std::string& resource,
344 ContentSetting* setting,
345 bool* uses_default_content_setting,
346 bool* is_managed) const {
347 scoped_ptr<base::Value> value;
348 content_settings::SettingInfo info;
349 bool uses_plugin_specific_setting = false;
350 if (ShouldUseJavaScriptSettingForPlugin(plugin)) {
352 host_content_settings_map_->GetWebsiteSetting(
353 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
354 std::string(), &info));
356 content_settings::SettingInfo specific_info;
357 scoped_ptr<base::Value> specific_setting(
358 host_content_settings_map_->GetWebsiteSetting(
359 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource,
361 content_settings::SettingInfo general_info;
362 scoped_ptr<base::Value> general_setting(
363 host_content_settings_map_->GetWebsiteSetting(
364 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS,
365 std::string(), &general_info));
367 // If there is a plugin-specific setting, we use it, unless the general
368 // setting was set by policy, in which case it takes precedence.
369 uses_plugin_specific_setting = specific_setting &&
370 (general_info.source != content_settings::SETTING_SOURCE_POLICY);
371 if (uses_plugin_specific_setting) {
372 value = specific_setting.Pass();
373 info = specific_info;
375 value = general_setting.Pass();
379 *setting = content_settings::ValueToContentSetting(value.get());
380 *uses_default_content_setting =
381 !uses_plugin_specific_setting &&
382 info.primary_pattern == ContentSettingsPattern::Wildcard() &&
383 info.secondary_pattern == ContentSettingsPattern::Wildcard();
384 *is_managed = info.source == content_settings::SETTING_SOURCE_POLICY;
387 void PluginInfoMessageFilter::Context::MaybeGrantAccess(
388 const ChromeViewHostMsg_GetPluginInfo_Status& status,
389 const base::FilePath& path) const {
390 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed ||
391 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) {
392 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin(
393 render_process_id_, path);