plugin_info_message_filter.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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_message_filter.h" 6 7#include "base/bind.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/plugins/chrome_plugin_service_filter.h" 15#include "chrome/browser/plugins/plugin_finder.h" 16#include "chrome/browser/plugins/plugin_metadata.h" 17#include "chrome/browser/plugins/plugin_prefs.h" 18#include "chrome/browser/profiles/profile.h" 19#include "chrome/common/chrome_content_client.h" 20#include "chrome/common/content_settings.h" 21#include "chrome/common/pref_names.h" 22#include "chrome/common/render_messages.h" 23#include "content/public/browser/browser_thread.h" 24#include "content/public/browser/plugin_service.h" 25#include "content/public/browser/plugin_service_filter.h" 26#include "url/gurl.h" 27 28#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. 29 30#if defined(ENABLE_EXTENSIONS) 31#include "chrome/browser/guest_view/web_view/web_view_renderer_state.h" 32#endif 33 34#if defined(OS_WIN) 35#include "base/win/metro.h" 36#endif 37 38using content::PluginService; 39using content::WebPluginInfo; 40 41namespace { 42 43// For certain sandboxed Pepper plugins, use the JavaScript Content Settings. 44bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) { 45 if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 46 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) { 47 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} // namespace 66 67PluginInfoMessageFilter::Context::Context(int render_process_id, 68 Profile* profile) 69 : render_process_id_(render_process_id), 70 resource_context_(profile->GetResourceContext()), 71 host_content_settings_map_(profile->GetHostContentSettingsMap()), 72 plugin_prefs_(PluginPrefs::GetForProfile(profile)) { 73 allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated, 74 profile->GetPrefs()); 75 allow_outdated_plugins_.MoveToThread( 76 content::BrowserThread::GetMessageLoopProxyForThread( 77 content::BrowserThread::IO)); 78 always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize, 79 profile->GetPrefs()); 80 always_authorize_plugins_.MoveToThread( 81 content::BrowserThread::GetMessageLoopProxyForThread( 82 content::BrowserThread::IO)); 83} 84 85PluginInfoMessageFilter::Context::~Context() { 86} 87 88PluginInfoMessageFilter::PluginInfoMessageFilter( 89 int render_process_id, 90 Profile* profile) 91 : BrowserMessageFilter(ChromeMsgStart), 92 context_(render_process_id, profile), 93 weak_ptr_factory_(this) { 94} 95 96bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) { 97 IPC_BEGIN_MESSAGE_MAP(PluginInfoMessageFilter, message) 98 IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo, 99 OnGetPluginInfo) 100#if defined(ENABLE_PEPPER_CDMS) 101 IPC_MESSAGE_HANDLER( 102 ChromeViewHostMsg_IsInternalPluginRegisteredForMimeType, 103 OnIsInternalPluginRegisteredForMimeType) 104#endif 105 IPC_MESSAGE_UNHANDLED(return false) 106 IPC_END_MESSAGE_MAP() 107 return true; 108} 109 110void PluginInfoMessageFilter::OnDestruct() const { 111 const_cast<PluginInfoMessageFilter*>(this)-> 112 weak_ptr_factory_.InvalidateWeakPtrs(); 113 114 // Destroy on the UI thread because we contain a |PrefMember|. 115 content::BrowserThread::DeleteOnUIThread::Destruct(this); 116} 117 118PluginInfoMessageFilter::~PluginInfoMessageFilter() {} 119 120struct PluginInfoMessageFilter::GetPluginInfo_Params { 121 int render_frame_id; 122 GURL url; 123 GURL top_origin_url; 124 std::string mime_type; 125}; 126 127void PluginInfoMessageFilter::OnGetPluginInfo( 128 int render_frame_id, 129 const GURL& url, 130 const GURL& top_origin_url, 131 const std::string& mime_type, 132 IPC::Message* reply_msg) { 133 GetPluginInfo_Params params = { 134 render_frame_id, 135 url, 136 top_origin_url, 137 mime_type 138 }; 139 PluginService::GetInstance()->GetPlugins( 140 base::Bind(&PluginInfoMessageFilter::PluginsLoaded, 141 weak_ptr_factory_.GetWeakPtr(), 142 params, reply_msg)); 143} 144 145void PluginInfoMessageFilter::PluginsLoaded( 146 const GetPluginInfo_Params& params, 147 IPC::Message* reply_msg, 148 const std::vector<WebPluginInfo>& plugins) { 149 ChromeViewHostMsg_GetPluginInfo_Output output; 150 // This also fills in |actual_mime_type|. 151 scoped_ptr<PluginMetadata> plugin_metadata; 152 if (context_.FindEnabledPlugin(params.render_frame_id, params.url, 153 params.top_origin_url, params.mime_type, 154 &output.status, &output.plugin, 155 &output.actual_mime_type, 156 &plugin_metadata)) { 157 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(), 158 &output.status); 159 } 160 161 if (plugin_metadata) { 162 output.group_identifier = plugin_metadata->identifier(); 163 output.group_name = plugin_metadata->name(); 164 } 165 166 context_.MaybeGrantAccess(output.status, output.plugin.path); 167 168 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output); 169 Send(reply_msg); 170} 171 172#if defined(ENABLE_PEPPER_CDMS) 173void PluginInfoMessageFilter::OnIsInternalPluginRegisteredForMimeType( 174 const std::string& mime_type, 175 bool* is_registered, 176 std::vector<base::string16>* additional_param_names, 177 std::vector<base::string16>* additional_param_values) { 178 std::vector<WebPluginInfo> plugins; 179 PluginService::GetInstance()->GetInternalPlugins(&plugins); 180 for (size_t i = 0; i < plugins.size(); ++i) { 181 const std::vector<content::WebPluginMimeType>& mime_types = 182 plugins[i].mime_types; 183 for (size_t j = 0; j < mime_types.size(); ++j) { 184 if (mime_types[j].mime_type == mime_type) { 185 *is_registered = true; 186 *additional_param_names = mime_types[j].additional_param_names; 187 *additional_param_values = mime_types[j].additional_param_values; 188 return; 189 } 190 } 191 } 192 193 *is_registered = false; 194} 195#endif 196 197void PluginInfoMessageFilter::Context::DecidePluginStatus( 198 const GetPluginInfo_Params& params, 199 const WebPluginInfo& plugin, 200 const PluginMetadata* plugin_metadata, 201 ChromeViewHostMsg_GetPluginInfo_Status* status) const { 202#if defined(OS_WIN) 203 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI && 204 base::win::IsMetroProcess()) { 205 status->value = 206 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 207 return; 208 } 209#endif 210 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { 211 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 212 // NPAPI plugins are not supported inside <webview> guests. 213#if defined(ENABLE_EXTENSIONS) 214 if (WebViewRendererState::GetInstance()->IsGuest(render_process_id_)) { 215 status->value = 216 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 217 return; 218 } 219#endif 220 } 221 222 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT; 223 bool uses_default_content_setting = true; 224 bool is_managed = false; 225 // Check plug-in content settings. The primary URL is the top origin URL and 226 // the secondary URL is the plug-in URL. 227 GetPluginContentSetting(plugin, params.top_origin_url, params.url, 228 plugin_metadata->identifier(), &plugin_setting, 229 &uses_default_content_setting, &is_managed); 230 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT); 231 232 PluginMetadata::SecurityStatus plugin_status = 233 plugin_metadata->GetSecurityStatus(plugin); 234#if defined(ENABLE_PLUGIN_INSTALLATION) 235 // Check if the plug-in is outdated. 236 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE && 237 !allow_outdated_plugins_.GetValue()) { 238 if (allow_outdated_plugins_.IsManaged()) { 239 status->value = 240 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed; 241 } else { 242 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked; 243 } 244 return; 245 } 246#endif 247 // Check if the plug-in or its group is enabled by policy. 248 PluginPrefs::PolicyStatus plugin_policy = 249 plugin_prefs_->PolicyStatusForPlugin(plugin.name); 250 PluginPrefs::PolicyStatus group_policy = 251 plugin_prefs_->PolicyStatusForPlugin(plugin_metadata->name()); 252 253 // Check if the plug-in requires authorization. 254 if (plugin_status == 255 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION && 256 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 257 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS && 258 !always_authorize_plugins_.GetValue() && 259 plugin_setting != CONTENT_SETTING_BLOCK && 260 uses_default_content_setting && 261 plugin_policy != PluginPrefs::POLICY_ENABLED && 262 group_policy != PluginPrefs::POLICY_ENABLED && 263 !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted( 264 plugin.path)) { 265 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 266 return; 267 } 268 269 // Check if the plug-in is crashing too much. 270 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) && 271 !always_authorize_plugins_.GetValue() && 272 plugin_setting != CONTENT_SETTING_BLOCK && 273 uses_default_content_setting) { 274 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 275 return; 276 } 277 278 if (plugin_setting == CONTENT_SETTING_ASK) { 279 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay; 280 } else if (plugin_setting == CONTENT_SETTING_BLOCK) { 281 status->value = 282 is_managed ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy 283 : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; 284 } 285 286 if (status->value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed) { 287 // Allow an embedder of <webview> to block a plugin from being loaded inside 288 // the guest. In order to do this, set the status to 'Unauthorized' here, 289 // and update the status as appropriate depending on the response from the 290 // embedder. 291#if defined(ENABLE_EXTENSIONS) 292 if (WebViewRendererState::GetInstance()->IsGuest(render_process_id_)) 293 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 294 295#endif 296 } 297} 298 299bool PluginInfoMessageFilter::Context::FindEnabledPlugin( 300 int render_frame_id, 301 const GURL& url, 302 const GURL& top_origin_url, 303 const std::string& mime_type, 304 ChromeViewHostMsg_GetPluginInfo_Status* status, 305 WebPluginInfo* plugin, 306 std::string* actual_mime_type, 307 scoped_ptr<PluginMetadata>* plugin_metadata) const { 308 bool allow_wildcard = true; 309 std::vector<WebPluginInfo> matching_plugins; 310 std::vector<std::string> mime_types; 311 PluginService::GetInstance()->GetPluginInfoArray( 312 url, mime_type, allow_wildcard, &matching_plugins, &mime_types); 313 if (matching_plugins.empty()) { 314 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; 315 return false; 316 } 317 318 content::PluginServiceFilter* filter = 319 PluginService::GetInstance()->GetFilter(); 320 size_t i = 0; 321 for (; i < matching_plugins.size(); ++i) { 322 if (!filter || filter->IsPluginAvailable(render_process_id_, 323 render_frame_id, 324 resource_context_, 325 url, 326 top_origin_url, 327 &matching_plugins[i])) { 328 break; 329 } 330 } 331 332 // If we broke out of the loop, we have found an enabled plug-in. 333 bool enabled = i < matching_plugins.size(); 334 if (!enabled) { 335 // Otherwise, we only found disabled plug-ins, so we take the first one. 336 i = 0; 337 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled; 338 } 339 340 *plugin = matching_plugins[i]; 341 *actual_mime_type = mime_types[i]; 342 if (plugin_metadata) 343 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); 344 345 return enabled; 346} 347 348void PluginInfoMessageFilter::Context::GetPluginContentSetting( 349 const WebPluginInfo& plugin, 350 const GURL& policy_url, 351 const GURL& plugin_url, 352 const std::string& resource, 353 ContentSetting* setting, 354 bool* uses_default_content_setting, 355 bool* is_managed) const { 356 scoped_ptr<base::Value> value; 357 content_settings::SettingInfo info; 358 bool uses_plugin_specific_setting = false; 359 if (ShouldUseJavaScriptSettingForPlugin(plugin)) { 360 value.reset( 361 host_content_settings_map_->GetWebsiteSetting( 362 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 363 std::string(), &info)); 364 } else { 365 content_settings::SettingInfo specific_info; 366 scoped_ptr<base::Value> specific_setting( 367 host_content_settings_map_->GetWebsiteSetting( 368 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource, 369 &specific_info)); 370 content_settings::SettingInfo general_info; 371 scoped_ptr<base::Value> general_setting( 372 host_content_settings_map_->GetWebsiteSetting( 373 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, 374 std::string(), &general_info)); 375 376 // If there is a plugin-specific setting, we use it, unless the general 377 // setting was set by policy, in which case it takes precedence. 378 uses_plugin_specific_setting = specific_setting && 379 (general_info.source != content_settings::SETTING_SOURCE_POLICY); 380 if (uses_plugin_specific_setting) { 381 value = specific_setting.Pass(); 382 info = specific_info; 383 } else { 384 value = general_setting.Pass(); 385 info = general_info; 386 } 387 } 388 *setting = content_settings::ValueToContentSetting(value.get()); 389 *uses_default_content_setting = 390 !uses_plugin_specific_setting && 391 info.primary_pattern == ContentSettingsPattern::Wildcard() && 392 info.secondary_pattern == ContentSettingsPattern::Wildcard(); 393 *is_managed = info.source == content_settings::SETTING_SOURCE_POLICY; 394} 395 396void PluginInfoMessageFilter::Context::MaybeGrantAccess( 397 const ChromeViewHostMsg_GetPluginInfo_Status& status, 398 const base::FilePath& path) const { 399 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed || 400 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) { 401 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin( 402 render_process_id_, path); 403 } 404} 405 406