plugin_info_message_filter.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// Copyright (c) 2012 The Chromium Authors. All rights reserved. 247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// Use of this source code is governed by a BSD-style license that can be 347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// found in the LICENSE file. 447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/plugins/plugin_info_message_filter.h" 647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "base/bind.h" 847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "base/memory/scoped_ptr.h" 947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "base/metrics/histogram.h" 1047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "base/prefs/pref_service.h" 1147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "base/strings/utf_string_conversions.h" 1247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/content_settings/content_settings_utils.h" 1347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/content_settings/host_content_settings_map.h" 1447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/plugins/chrome_plugin_service_filter.h" 1547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/plugins/plugin_finder.h" 1647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/plugins/plugin_metadata.h" 1747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/plugins/plugin_prefs.h" 1847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/profiles/profile.h" 1947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/common/chrome_content_client.h" 2047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/common/content_settings.h" 2147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/common/pref_names.h" 2247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/common/render_messages.h" 2347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "content/public/browser/browser_thread.h" 2447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "content/public/browser/plugin_service.h" 2547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "content/public/browser/plugin_service_filter.h" 2647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "url/gurl.h" 2747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 2847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. 2947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 3047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#if defined(ENABLE_EXTENSIONS) 3147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "chrome/browser/guest_view/web_view/web_view_renderer_state.h" 3247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#endif 3347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 3447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#if defined(OS_WIN) 3547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "base/win/metro.h" 3647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#endif 3747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 3847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgusing content::PluginService; 3947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgusing content::WebPluginInfo; 4047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 4147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgnamespace { 4247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org 4347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// For certain sandboxed Pepper plugins, use the JavaScript Content Settings. 4447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgbool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) { 4547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 4647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) { 4747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org return false; 48 } 49 50 // Treat Native Client invocations like JavaScript. 51 if (plugin.name == base::ASCIIToUTF16(ChromeContentClient::kNaClPluginName)) 52 return true; 53 54#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) 55 // Treat CDM invocations like JavaScript. 56 if (plugin.name == base::ASCIIToUTF16(kWidevineCdmDisplayName)) { 57 DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS); 58 return true; 59 } 60#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) 61 62 return false; 63} 64 65#if defined(ENABLE_PEPPER_CDMS) 66 67enum PluginAvailabilityStatusForUMA { 68 PLUGIN_NOT_REGISTERED, 69 PLUGIN_AVAILABLE, 70 PLUGIN_DISABLED, 71 PLUGIN_AVAILABILITY_STATUS_MAX 72}; 73 74static void SendPluginAvailabilityUMA(const std::string& mime_type, 75 PluginAvailabilityStatusForUMA status) { 76#if defined(WIDEVINE_CDM_AVAILABLE) 77 // Only report results for Widevine CDM. 78 if (mime_type != kWidevineCdmPluginMimeType) 79 return; 80 81 UMA_HISTOGRAM_ENUMERATION("Plugin.AvailabilityStatus.WidevineCdm", 82 status, PLUGIN_AVAILABILITY_STATUS_MAX); 83#endif // defined(WIDEVINE_CDM_AVAILABLE) 84} 85 86#endif // defined(ENABLE_PEPPER_CDMS) 87 88} // namespace 89 90PluginInfoMessageFilter::Context::Context(int render_process_id, 91 Profile* profile) 92 : render_process_id_(render_process_id), 93 resource_context_(profile->GetResourceContext()), 94 host_content_settings_map_(profile->GetHostContentSettingsMap()), 95 plugin_prefs_(PluginPrefs::GetForProfile(profile)) { 96 allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated, 97 profile->GetPrefs()); 98 allow_outdated_plugins_.MoveToThread( 99 content::BrowserThread::GetMessageLoopProxyForThread( 100 content::BrowserThread::IO)); 101 always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize, 102 profile->GetPrefs()); 103 always_authorize_plugins_.MoveToThread( 104 content::BrowserThread::GetMessageLoopProxyForThread( 105 content::BrowserThread::IO)); 106} 107 108PluginInfoMessageFilter::Context::~Context() { 109} 110 111PluginInfoMessageFilter::PluginInfoMessageFilter( 112 int render_process_id, 113 Profile* profile) 114 : BrowserMessageFilter(ChromeMsgStart), 115 context_(render_process_id, profile), 116 weak_ptr_factory_(this) { 117} 118 119bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) { 120 IPC_BEGIN_MESSAGE_MAP(PluginInfoMessageFilter, message) 121 IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo, 122 OnGetPluginInfo) 123#if defined(ENABLE_PEPPER_CDMS) 124 IPC_MESSAGE_HANDLER( 125 ChromeViewHostMsg_IsInternalPluginAvailableForMimeType, 126 OnIsInternalPluginAvailableForMimeType) 127#endif 128 IPC_MESSAGE_UNHANDLED(return false) 129 IPC_END_MESSAGE_MAP() 130 return true; 131} 132 133void PluginInfoMessageFilter::OnDestruct() const { 134 const_cast<PluginInfoMessageFilter*>(this)-> 135 weak_ptr_factory_.InvalidateWeakPtrs(); 136 137 // Destroy on the UI thread because we contain a |PrefMember|. 138 content::BrowserThread::DeleteOnUIThread::Destruct(this); 139} 140 141PluginInfoMessageFilter::~PluginInfoMessageFilter() {} 142 143struct PluginInfoMessageFilter::GetPluginInfo_Params { 144 int render_frame_id; 145 GURL url; 146 GURL top_origin_url; 147 std::string mime_type; 148}; 149 150void PluginInfoMessageFilter::OnGetPluginInfo( 151 int render_frame_id, 152 const GURL& url, 153 const GURL& top_origin_url, 154 const std::string& mime_type, 155 IPC::Message* reply_msg) { 156 GetPluginInfo_Params params = { 157 render_frame_id, 158 url, 159 top_origin_url, 160 mime_type 161 }; 162 PluginService::GetInstance()->GetPlugins( 163 base::Bind(&PluginInfoMessageFilter::PluginsLoaded, 164 weak_ptr_factory_.GetWeakPtr(), 165 params, reply_msg)); 166} 167 168void PluginInfoMessageFilter::PluginsLoaded( 169 const GetPluginInfo_Params& params, 170 IPC::Message* reply_msg, 171 const std::vector<WebPluginInfo>& plugins) { 172 ChromeViewHostMsg_GetPluginInfo_Output output; 173 // This also fills in |actual_mime_type|. 174 scoped_ptr<PluginMetadata> plugin_metadata; 175 if (context_.FindEnabledPlugin(params.render_frame_id, params.url, 176 params.top_origin_url, params.mime_type, 177 &output.status, &output.plugin, 178 &output.actual_mime_type, 179 &plugin_metadata)) { 180 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(), 181 &output.status); 182 } 183 184 if (plugin_metadata) { 185 output.group_identifier = plugin_metadata->identifier(); 186 output.group_name = plugin_metadata->name(); 187 } 188 189 context_.MaybeGrantAccess(output.status, output.plugin.path); 190 191 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output); 192 Send(reply_msg); 193} 194 195#if defined(ENABLE_PEPPER_CDMS) 196 197void PluginInfoMessageFilter::OnIsInternalPluginAvailableForMimeType( 198 const std::string& mime_type, 199 bool* is_available, 200 std::vector<base::string16>* additional_param_names, 201 std::vector<base::string16>* additional_param_values) { 202 std::vector<WebPluginInfo> plugins; 203 PluginService::GetInstance()->GetInternalPlugins(&plugins); 204 205 bool is_plugin_disabled = false; 206 for (size_t i = 0; i < plugins.size(); ++i) { 207 const WebPluginInfo& plugin = plugins[i]; 208 const std::vector<content::WebPluginMimeType>& mime_types = 209 plugin.mime_types; 210 for (size_t j = 0; j < mime_types.size(); ++j) { 211 if (mime_types[j].mime_type == mime_type) { 212 if (!context_.IsPluginEnabled(plugin)) { 213 is_plugin_disabled = true; 214 break; 215 } 216 217 *is_available = true; 218 *additional_param_names = mime_types[j].additional_param_names; 219 *additional_param_values = mime_types[j].additional_param_values; 220 SendPluginAvailabilityUMA(mime_type, PLUGIN_AVAILABLE); 221 return; 222 } 223 } 224 } 225 226 *is_available = false; 227 SendPluginAvailabilityUMA( 228 mime_type, is_plugin_disabled ? PLUGIN_DISABLED : PLUGIN_NOT_REGISTERED); 229} 230 231#endif // defined(ENABLE_PEPPER_CDMS) 232 233void PluginInfoMessageFilter::Context::DecidePluginStatus( 234 const GetPluginInfo_Params& params, 235 const WebPluginInfo& plugin, 236 const PluginMetadata* plugin_metadata, 237 ChromeViewHostMsg_GetPluginInfo_Status* status) const { 238#if defined(OS_WIN) 239 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI && 240 base::win::IsMetroProcess()) { 241 status->value = 242 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 243 return; 244 } 245#endif 246 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { 247 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 248 // NPAPI plugins are not supported inside <webview> guests. 249#if defined(ENABLE_EXTENSIONS) 250 if (extensions::WebViewRendererState::GetInstance()->IsGuest( 251 render_process_id_)) { 252 status->value = 253 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 254 return; 255 } 256#endif 257 } 258 259 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT; 260 bool uses_default_content_setting = true; 261 bool is_managed = false; 262 // Check plug-in content settings. The primary URL is the top origin URL and 263 // the secondary URL is the plug-in URL. 264 GetPluginContentSetting(plugin, params.top_origin_url, params.url, 265 plugin_metadata->identifier(), &plugin_setting, 266 &uses_default_content_setting, &is_managed); 267 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT); 268 269 PluginMetadata::SecurityStatus plugin_status = 270 plugin_metadata->GetSecurityStatus(plugin); 271#if defined(ENABLE_PLUGIN_INSTALLATION) 272 // Check if the plug-in is outdated. 273 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE && 274 !allow_outdated_plugins_.GetValue()) { 275 if (allow_outdated_plugins_.IsManaged()) { 276 status->value = 277 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed; 278 } else { 279 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked; 280 } 281 return; 282 } 283#endif 284 // Check if the plug-in or its group is enabled by policy. 285 PluginPrefs::PolicyStatus plugin_policy = 286 plugin_prefs_->PolicyStatusForPlugin(plugin.name); 287 PluginPrefs::PolicyStatus group_policy = 288 plugin_prefs_->PolicyStatusForPlugin(plugin_metadata->name()); 289 290 // Check if the plug-in requires authorization. 291 if (plugin_status == 292 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION && 293 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 294 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS && 295 !always_authorize_plugins_.GetValue() && 296 plugin_setting != CONTENT_SETTING_BLOCK && 297 uses_default_content_setting && 298 plugin_policy != PluginPrefs::POLICY_ENABLED && 299 group_policy != PluginPrefs::POLICY_ENABLED && 300 !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted( 301 plugin.path)) { 302 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 303 return; 304 } 305 306 // Check if the plug-in is crashing too much. 307 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) && 308 !always_authorize_plugins_.GetValue() && 309 plugin_setting != CONTENT_SETTING_BLOCK && 310 uses_default_content_setting) { 311 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 312 return; 313 } 314 315 if (plugin_setting == CONTENT_SETTING_ASK) { 316 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay; 317 } else if (plugin_setting == CONTENT_SETTING_BLOCK) { 318 status->value = 319 is_managed ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy 320 : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; 321 } 322 323 if (status->value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed) { 324 // Allow an embedder of <webview> to block a plugin from being loaded inside 325 // the guest. In order to do this, set the status to 'Unauthorized' here, 326 // and update the status as appropriate depending on the response from the 327 // embedder. 328#if defined(ENABLE_EXTENSIONS) 329 if (extensions::WebViewRendererState::GetInstance()->IsGuest( 330 render_process_id_)) 331 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 332 333#endif 334 } 335} 336 337bool PluginInfoMessageFilter::Context::FindEnabledPlugin( 338 int render_frame_id, 339 const GURL& url, 340 const GURL& top_origin_url, 341 const std::string& mime_type, 342 ChromeViewHostMsg_GetPluginInfo_Status* status, 343 WebPluginInfo* plugin, 344 std::string* actual_mime_type, 345 scoped_ptr<PluginMetadata>* plugin_metadata) const { 346 bool allow_wildcard = true; 347 std::vector<WebPluginInfo> matching_plugins; 348 std::vector<std::string> mime_types; 349 PluginService::GetInstance()->GetPluginInfoArray( 350 url, mime_type, allow_wildcard, &matching_plugins, &mime_types); 351 if (matching_plugins.empty()) { 352 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; 353 return false; 354 } 355 356 content::PluginServiceFilter* filter = 357 PluginService::GetInstance()->GetFilter(); 358 size_t i = 0; 359 for (; i < matching_plugins.size(); ++i) { 360 if (!filter || filter->IsPluginAvailable(render_process_id_, 361 render_frame_id, 362 resource_context_, 363 url, 364 top_origin_url, 365 &matching_plugins[i])) { 366 break; 367 } 368 } 369 370 // If we broke out of the loop, we have found an enabled plug-in. 371 bool enabled = i < matching_plugins.size(); 372 if (!enabled) { 373 // Otherwise, we only found disabled plug-ins, so we take the first one. 374 i = 0; 375 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled; 376 } 377 378 *plugin = matching_plugins[i]; 379 *actual_mime_type = mime_types[i]; 380 if (plugin_metadata) 381 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); 382 383 return enabled; 384} 385 386void PluginInfoMessageFilter::Context::GetPluginContentSetting( 387 const WebPluginInfo& plugin, 388 const GURL& policy_url, 389 const GURL& plugin_url, 390 const std::string& resource, 391 ContentSetting* setting, 392 bool* uses_default_content_setting, 393 bool* is_managed) const { 394 scoped_ptr<base::Value> value; 395 content_settings::SettingInfo info; 396 bool uses_plugin_specific_setting = false; 397 if (ShouldUseJavaScriptSettingForPlugin(plugin)) { 398 value.reset( 399 host_content_settings_map_->GetWebsiteSetting( 400 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 401 std::string(), &info)); 402 } else { 403 content_settings::SettingInfo specific_info; 404 scoped_ptr<base::Value> specific_setting( 405 host_content_settings_map_->GetWebsiteSetting( 406 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource, 407 &specific_info)); 408 content_settings::SettingInfo general_info; 409 scoped_ptr<base::Value> general_setting( 410 host_content_settings_map_->GetWebsiteSetting( 411 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, 412 std::string(), &general_info)); 413 414 // If there is a plugin-specific setting, we use it, unless the general 415 // setting was set by policy, in which case it takes precedence. 416 uses_plugin_specific_setting = specific_setting && 417 (general_info.source != content_settings::SETTING_SOURCE_POLICY); 418 if (uses_plugin_specific_setting) { 419 value = specific_setting.Pass(); 420 info = specific_info; 421 } else { 422 value = general_setting.Pass(); 423 info = general_info; 424 } 425 } 426 *setting = content_settings::ValueToContentSetting(value.get()); 427 *uses_default_content_setting = 428 !uses_plugin_specific_setting && 429 info.primary_pattern == ContentSettingsPattern::Wildcard() && 430 info.secondary_pattern == ContentSettingsPattern::Wildcard(); 431 *is_managed = info.source == content_settings::SETTING_SOURCE_POLICY; 432} 433 434void PluginInfoMessageFilter::Context::MaybeGrantAccess( 435 const ChromeViewHostMsg_GetPluginInfo_Status& status, 436 const base::FilePath& path) const { 437 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed || 438 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) { 439 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin( 440 render_process_id_, path); 441 } 442} 443 444bool PluginInfoMessageFilter::Context::IsPluginEnabled( 445 const content::WebPluginInfo& plugin) const { 446 return plugin_prefs_->IsPluginEnabled(plugin); 447} 448