Enforcing in the browser process which origins may embed the PDF plugin.
Before this CL, the PDF plugin process would enforce that it may only be
embedded inside the PDF extension's origin
(mhjfbmdgcfjbbpaeojofohoefgiehjai) and inside chrome://print. After
this CL, this enforcement is done in the browser process instead. The
earlier enforcement helps add security assertions that follow-up CLs may
depend on - for example see:
https://crrev.com/c/1986923/15/content/browser/plugin_service_impl.cc#211
The already existing PDFExtensionTest.EnsureInternalPluginDisabled test
makes sure that the PDF plugin may not be embedded in a data: URL.
Bug: 1027173
Change-Id: I0d1d47d9dee7de92d1f3cbb44a72d38b14236f69
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2003262
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Reviewed-by: Charlie Reis <creis@chromium.org>
Commit-Queue: Łukasz Anforowicz <lukasza@chromium.org>
Cr-Commit-Position: refs/heads/master@{#733121}
This commit is contained in:

committed by
Commit Bot

parent
39b342a19b
commit
4600ea3f8c
chrome/browser
content
browser
bad_message.h
frame_host
plugin_service_impl.ccplugin_service_impl.hplugin_service_impl_browsertest.cccommon
public
renderer
pepper
pdf
tools/metrics/histograms
@ -159,6 +159,7 @@
|
||||
#include "chrome/common/buildflags.h"
|
||||
#include "chrome/common/channel_info.h"
|
||||
#include "chrome/common/chrome_constants.h"
|
||||
#include "chrome/common/chrome_content_client.h"
|
||||
#include "chrome/common/chrome_features.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/chrome_paths_internal.h"
|
||||
@ -309,6 +310,7 @@
|
||||
#include "net/http/http_util.h"
|
||||
#include "net/ssl/client_cert_store.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "pdf/buildflags.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "ppapi/host/ppapi_host.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
@ -5488,3 +5490,32 @@ void ChromeContentBrowserClient::IsClipboardPasteAllowed(
|
||||
std::move(callback).Run(ClipboardPasteAllowed(true));
|
||||
#endif // BUILDFLAG(FULL_SAFE_BROWSING)
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
bool ChromeContentBrowserClient::ShouldAllowPluginCreation(
|
||||
const url::Origin& embedder_origin,
|
||||
const content::PepperPluginInfo& plugin_info) {
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
if (plugin_info.name == ChromeContentClient::kPDFInternalPluginName) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
// Allow embedding the internal PDF plugin in the built-in PDF extension.
|
||||
if (embedder_origin.scheme() == extensions::kExtensionScheme &&
|
||||
embedder_origin.host() == extension_misc::kPdfExtensionId) {
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
|
||||
// Allow embedding the internal PDF plugin in chrome://print.
|
||||
if (embedder_origin == url::Origin::Create(GURL(chrome::kChromeUIPrintURL)))
|
||||
return true;
|
||||
|
||||
// Only allow the PDF plugin in the known, trustworthy origins that are
|
||||
// allowlisted above. See also https://crbug.com/520422 and
|
||||
// https://crbug.com/1027173.
|
||||
return false;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PLUGINS)
|
||||
|
@ -648,6 +648,12 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
|
||||
const std::string& data,
|
||||
IsClipboardPasteAllowedCallback callback) override;
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
bool ShouldAllowPluginCreation(
|
||||
const url::Origin& embedder_origin,
|
||||
const content::PepperPluginInfo& plugin_info) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context);
|
||||
static bool HandleWebUIReverse(GURL* url,
|
||||
|
@ -251,6 +251,7 @@ enum BadMessageReason {
|
||||
RFH_UNEXPECTED_EMBEDDING_TOKEN = 223,
|
||||
RFH_MISSING_EMBEDDING_TOKEN = 224,
|
||||
RFH_BAD_DOCUMENT_POLICY_HEADER = 225,
|
||||
RFMF_INVALID_PLUGIN_EMBEDDER_ORIGIN = 226,
|
||||
|
||||
// Please add new elements here. The naming convention is abbreviated class
|
||||
// name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
|
||||
|
@ -320,12 +320,24 @@ void RenderFrameMessageFilter::OnGetPluginInfo(
|
||||
}
|
||||
|
||||
void RenderFrameMessageFilter::OnOpenChannelToPepperPlugin(
|
||||
const url::Origin& embedder_origin,
|
||||
const base::FilePath& path,
|
||||
const base::Optional<url::Origin>& origin_lock,
|
||||
IPC::Message* reply_msg) {
|
||||
// Enforce that the sender of the IPC (i.e. |render_process_id_|) is actually
|
||||
// able/allowed to host a frame with |embedder_origin|.
|
||||
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
|
||||
if (!embedder_origin.opaque() &&
|
||||
!policy->CanAccessDataForOrigin(render_process_id_, embedder_origin)) {
|
||||
NOTREACHED() << embedder_origin;
|
||||
bad_message::ReceivedBadMessage(
|
||||
this, bad_message::RFMF_INVALID_PLUGIN_EMBEDDER_ORIGIN);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin_service_->OpenChannelToPpapiPlugin(
|
||||
render_process_id_, path, profile_data_directory_, origin_lock,
|
||||
new OpenChannelToPpapiPluginCallback(this, reply_msg));
|
||||
render_process_id_, embedder_origin, path, profile_data_directory_,
|
||||
origin_lock, new OpenChannelToPpapiPluginCallback(this, reply_msg));
|
||||
}
|
||||
|
||||
void RenderFrameMessageFilter::OnDidCreateOutOfProcessPepperInstance(
|
||||
|
@ -96,6 +96,7 @@ class CONTENT_EXPORT RenderFrameMessageFilter : public BrowserMessageFilter {
|
||||
WebPluginInfo* info,
|
||||
std::string* actual_mime_type);
|
||||
void OnOpenChannelToPepperPlugin(
|
||||
const url::Origin& embedder_origin,
|
||||
const base::FilePath& path,
|
||||
const base::Optional<url::Origin>& origin_lock,
|
||||
IPC::Message* reply_msg);
|
||||
|
@ -166,6 +166,7 @@ PpapiPluginProcessHost* PluginServiceImpl::FindPpapiBrokerProcess(
|
||||
|
||||
PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess(
|
||||
int render_process_id,
|
||||
const url::Origin& embedder_origin,
|
||||
const base::FilePath& plugin_path,
|
||||
const base::FilePath& profile_data_directory,
|
||||
const base::Optional<url::Origin>& origin_lock) {
|
||||
@ -184,6 +185,12 @@ PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Validate that |embedder_origin| is allowed to embed the plugin.
|
||||
if (!GetContentClient()->browser()->ShouldAllowPluginCreation(embedder_origin,
|
||||
*info)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Flash has its own flavour of CORS, so CORB needs to allow all responses
|
||||
// and rely on Flash to enforce same-origin policy. See also
|
||||
// https://crbug.com/874515 and https://crbug.com/816318#c5.
|
||||
@ -256,12 +263,14 @@ PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess(
|
||||
|
||||
void PluginServiceImpl::OpenChannelToPpapiPlugin(
|
||||
int render_process_id,
|
||||
const url::Origin& embedder_origin,
|
||||
const base::FilePath& plugin_path,
|
||||
const base::FilePath& profile_data_directory,
|
||||
const base::Optional<url::Origin>& origin_lock,
|
||||
PpapiPluginProcessHost::PluginClient* client) {
|
||||
PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(
|
||||
render_process_id, plugin_path, profile_data_directory, origin_lock);
|
||||
render_process_id, embedder_origin, plugin_path, profile_data_directory,
|
||||
origin_lock);
|
||||
if (plugin_host) {
|
||||
plugin_host->OpenChannelToPlugin(client);
|
||||
} else {
|
||||
|
@ -87,6 +87,7 @@ class CONTENT_EXPORT PluginServiceImpl : public PluginService {
|
||||
// is NULL. Must be called on the IO thread.
|
||||
PpapiPluginProcessHost* FindOrStartPpapiPluginProcess(
|
||||
int render_process_id,
|
||||
const url::Origin& embedder_origin,
|
||||
const base::FilePath& plugin_path,
|
||||
const base::FilePath& profile_data_directory,
|
||||
const base::Optional<url::Origin>& origin_lock);
|
||||
@ -97,6 +98,7 @@ class CONTENT_EXPORT PluginServiceImpl : public PluginService {
|
||||
// a new plugin process if necessary. This must be called on the IO thread
|
||||
// or else a deadlock can occur.
|
||||
void OpenChannelToPpapiPlugin(int render_process_id,
|
||||
const url::Origin& embedder_origin,
|
||||
const base::FilePath& plugin_path,
|
||||
const base::FilePath& profile_data_directory,
|
||||
const base::Optional<url::Origin>& origin_lock,
|
||||
|
@ -88,8 +88,9 @@ class PluginServiceImplBrowserTest : public ContentBrowserTest {
|
||||
base::PostTask(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&PluginServiceImpl::OpenChannelToPpapiPlugin,
|
||||
base::Unretained(service), 0, plugin_path_, profile_dir_,
|
||||
origin, base::Unretained(client)));
|
||||
base::Unretained(service), /*render_process_id=*/0,
|
||||
/*embedder_origin=*/url::Origin(), plugin_path_,
|
||||
profile_dir_, origin, base::Unretained(client)));
|
||||
client->WaitForQuit();
|
||||
client->SetRunLoop(nullptr);
|
||||
}
|
||||
|
@ -983,8 +983,16 @@ IPC_MESSAGE_ROUTED1(FrameHostMsg_PluginContentOriginAllowed,
|
||||
// used to identify the proper process when the renderer notifies it that the
|
||||
// plugin is hung.
|
||||
//
|
||||
// |embedder_origin| provides the origin of the frame that embeds the plugin
|
||||
// (i.e. the origin of the document that contains the <embed> html tag).
|
||||
// |embedder_origin| needs to be included in the message payload, because the
|
||||
// message is received and handled on the IO thread in the browser process
|
||||
// (where it is not possible to consult
|
||||
// RenderFrameHostImpl::GetLastCommittedOrigin).
|
||||
//
|
||||
// On error an empty string and null handles are returned.
|
||||
IPC_SYNC_MESSAGE_CONTROL2_3(FrameHostMsg_OpenChannelToPepperPlugin,
|
||||
IPC_SYNC_MESSAGE_CONTROL3_3(FrameHostMsg_OpenChannelToPepperPlugin,
|
||||
url::Origin /* embedder_origin */,
|
||||
base::FilePath /* path */,
|
||||
base::Optional<url::Origin>, /* origin_lock */
|
||||
IPC::ChannelHandle /* handle to channel */,
|
||||
|
@ -1039,4 +1039,12 @@ void ContentBrowserClient::IsClipboardPasteAllowed(
|
||||
std::move(callback).Run(ClipboardPasteAllowed(true));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
bool ContentBrowserClient::ShouldAllowPluginCreation(
|
||||
const url::Origin& embedder_origin,
|
||||
const content::PepperPluginInfo& plugin_info) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace content
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/util/type_safety/strong_alias.h"
|
||||
#include "build/build_config.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/public/browser/certificate_request_result_type.h"
|
||||
#include "content/public/browser/generated_code_cache_settings.h"
|
||||
@ -36,6 +37,7 @@
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "services/network/public/mojom/network_context.mojom-forward.h"
|
||||
#include "services/network/public/mojom/restricted_cookie_manager.mojom-forward.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
|
||||
@ -59,6 +61,10 @@
|
||||
#include "content/public/browser/posix_file_descriptor_info.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
#include "content/public/common/pepper_plugin_info.h"
|
||||
#endif
|
||||
|
||||
namespace net {
|
||||
class AuthCredentials;
|
||||
class SiteForCookies;
|
||||
@ -1777,6 +1783,14 @@ class CONTENT_EXPORT ContentBrowserClient {
|
||||
const ui::ClipboardFormatType& data_type,
|
||||
const std::string& data,
|
||||
IsClipboardPasteAllowedCallback callback);
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
// Returns true if |embedder_origin| is allowed to embed a plugin described by
|
||||
// |plugin_info|. This method allows restricting some internal plugins (like
|
||||
// Chrome's PDF plugin) to specific origins.
|
||||
virtual bool ShouldAllowPluginCreation(const url::Origin& embedder_origin,
|
||||
const PepperPluginInfo& plugin_info);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "content/renderer/pepper/ppb_var_deprecated_impl.h"
|
||||
#include "content/renderer/pepper/ppb_video_decoder_impl.h"
|
||||
#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
|
||||
#include "content/renderer/render_frame_impl.h"
|
||||
#include "content/renderer/render_thread_impl.h"
|
||||
#include "content/renderer/render_view_impl.h"
|
||||
#include "ppapi/c/dev/ppb_audio_input_dev.h"
|
||||
@ -143,6 +144,8 @@
|
||||
#include "ppapi/thunk/enter.h"
|
||||
#include "ppapi/thunk/ppb_graphics_2d_api.h"
|
||||
#include "ppapi/thunk/thunk.h"
|
||||
#include "third_party/blink/public/platform/web_security_origin.h"
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
|
||||
using ppapi::InputEventData;
|
||||
using ppapi::PpapiGlobals;
|
||||
@ -805,7 +808,8 @@ scoped_refptr<PluginModule> PluginModule::Create(
|
||||
base::ProcessId peer_pid = 0;
|
||||
int plugin_child_id = 0;
|
||||
render_frame->Send(new FrameHostMsg_OpenChannelToPepperPlugin(
|
||||
path, origin_lock, &channel_handle, &peer_pid, &plugin_child_id));
|
||||
render_frame->GetWebFrame()->GetSecurityOrigin(), path, origin_lock,
|
||||
&channel_handle, &peer_pid, &plugin_child_id));
|
||||
if (!channel_handle.is_mojo_channel_handle()) {
|
||||
// Couldn't be initialized.
|
||||
return scoped_refptr<PluginModule>();
|
||||
|
@ -479,18 +479,21 @@ OutOfProcessInstance::~OutOfProcessInstance() {
|
||||
bool OutOfProcessInstance::Init(uint32_t argc,
|
||||
const char* argn[],
|
||||
const char* argv[]) {
|
||||
// Check if the PDF is being loaded in the PDF chrome extension. We only allow
|
||||
// the plugin to be loaded in the extension and print preview to avoid
|
||||
// exposing sensitive APIs directly to external websites.
|
||||
pp::Var document_url_var = pp::URLUtil_Dev::Get()->GetDocumentURL(this);
|
||||
if (!document_url_var.is_string())
|
||||
return false;
|
||||
|
||||
// Check if the PDF is being loaded in the PDF chrome extension. We only allow
|
||||
// the plugin to be loaded in the extension and print preview to avoid
|
||||
// exposing sensitive APIs directly to external websites.
|
||||
//
|
||||
// This is enforced before launching the plugin process (see
|
||||
// ChromeContentBrowserClient::ShouldAllowPluginCreation), so below we just do
|
||||
// a CHECK as a defense-in-depth.
|
||||
std::string document_url = document_url_var.AsString();
|
||||
base::StringPiece document_url_piece(document_url);
|
||||
is_print_preview_ = IsPrintPreviewUrl(document_url_piece);
|
||||
if (!document_url_piece.starts_with(kChromeExtension) && !is_print_preview_)
|
||||
return false;
|
||||
CHECK(document_url_piece.starts_with(kChromeExtension) || is_print_preview_);
|
||||
|
||||
// Check if the plugin is full frame. This is passed in from JS.
|
||||
for (uint32_t i = 0; i < argc; ++i) {
|
||||
|
@ -5429,6 +5429,8 @@ Unknown properties are collapsed to zero. -->
|
||||
<int value="222" label="RFPH_ADVANCE_FOCUS_INTO_PORTAL"/>
|
||||
<int value="223" label="RFH_UNEXPECTED_EMBEDDING_TOKEN"/>
|
||||
<int value="224" label="RFH_MISSING_EMBEDDING_TOKEN"/>
|
||||
<int value="225" label="RFH_BAD_DOCUMENT_POLICY_HEADER"/>
|
||||
<int value="226" label="RFMF_INVALID_PLUGIN_EMBEDDER_ORIGIN"/>
|
||||
</enum>
|
||||
|
||||
<enum name="BadMessageReasonExtensions">
|
||||
|
Reference in New Issue
Block a user