blob: 5a169e6139a2475ab9bfd4f39d1d7831c5735a0f [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/pepper/url_request_info_util.h"
#include <stddef.h>
#include <stdint.h>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "content/common/fileapi/file_system_messages.h"
#include "content/renderer/loader/request_extra_data.h"
#include "content/renderer/pepper/host_globals.h"
#include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
#include "content/renderer/pepper/plugin_module.h"
#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "net/http/http_util.h"
#include "ppapi/c/pp_bool.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/url_request_info_data.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/thunk/enter.h"
#include "third_party/WebKit/public/platform/FilePathConversion.h"
#include "third_party/WebKit/public/platform/WebData.h"
#include "third_party/WebKit/public/platform/WebHTTPBody.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "url/gurl.h"
#include "url/url_util.h"
using ppapi::Resource;
using ppapi::URLRequestInfoData;
using ppapi::thunk::EnterResourceNoLock;
using blink::WebData;
using blink::WebHTTPBody;
using blink::WebString;
using blink::WebLocalFrame;
using blink::WebURL;
using blink::WebURLRequest;
namespace content {
namespace {
// Appends the file ref given the Resource pointer associated with it to the
// given HTTP body, returning true on success.
bool AppendFileRefToBody(PP_Instance instance,
PP_Resource resource,
int64_t start_offset,
int64_t number_of_bytes,
PP_Time expected_last_modified_time,
WebHTTPBody* http_body) {
base::FilePath platform_path;
PepperPluginInstanceImpl* instance_impl =
HostGlobals::Get()->GetInstance(instance);
if (!instance_impl)
return false;
RendererPpapiHost* renderer_ppapi_host =
instance_impl->module()->renderer_ppapi_host();
if (!renderer_ppapi_host)
return false;
ppapi::host::ResourceHost* resource_host =
renderer_ppapi_host->GetPpapiHost()->GetResourceHost(resource);
if (!resource_host || !resource_host->IsFileRefHost())
return false;
PepperFileRefRendererHost* file_ref_host =
static_cast<PepperFileRefRendererHost*>(resource_host);
switch (file_ref_host->GetFileSystemType()) {
case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
// TODO(kinuko): remove this sync IPC when we fully support
// AppendURLRange for FileSystem URL.
RenderThreadImpl::current()->Send(
new FileSystemHostMsg_SyncGetPlatformPath(
file_ref_host->GetFileSystemURL(), &platform_path));
break;
case PP_FILESYSTEMTYPE_EXTERNAL:
platform_path = file_ref_host->GetExternalFilePath();
break;
default:
NOTREACHED();
}
http_body->AppendFileRange(blink::FilePathToWebString(platform_path),
start_offset, number_of_bytes,
expected_last_modified_time);
return true;
}
// Checks that the request data is valid. Returns false on failure. Note that
// method and header validation is done by the URL loader when the request is
// opened, and any access errors are returned asynchronously.
bool ValidateURLRequestData(const URLRequestInfoData& data) {
if (data.prefetch_buffer_lower_threshold < 0 ||
data.prefetch_buffer_upper_threshold < 0 ||
data.prefetch_buffer_upper_threshold <=
data.prefetch_buffer_lower_threshold) {
return false;
}
return true;
}
std::string FilterStringForXRequestedWithValue(const std::string& s) {
std::string rv;
rv.reserve(s.length());
for (size_t i = 0; i < s.length(); i++) {
char c = s[i];
// Allow ASCII digits, letters, periods, commas, and underscores. (Ignore
// all other characters.)
if (base::IsAsciiDigit(c) || base::IsAsciiAlpha(c) || (c == '.') ||
(c == ',') || (c == '_'))
rv.push_back(c);
}
return rv;
}
// Returns an appropriate value for the X-Requested-With header for plugins that
// present an X-Requested-With header. Returns a blank string for other plugins.
// We produce a user-agent-like string (eating spaces and other undesired
// characters) like "ShockwaveFlash/11.5.31.135" from the plugin name and
// version.
std::string MakeXRequestedWithValue(const std::string& name,
const std::string& version) {
std::string rv = FilterStringForXRequestedWithValue(name);
if (rv.empty())
return std::string();
// Apply to a narrow list of plugins only.
if (rv != "ShockwaveFlash" && rv != "PPAPITests")
return std::string();
std::string filtered_version = FilterStringForXRequestedWithValue(version);
if (!filtered_version.empty())
rv += "/" + filtered_version;
return rv;
}
} // namespace
bool CreateWebURLRequest(PP_Instance instance,
URLRequestInfoData* data,
WebLocalFrame* frame,
WebURLRequest* dest) {
// In the out-of-process case, we've received the URLRequestInfoData
// from the untrusted plugin and done no validation on it. We need to be
// sure it's not being malicious by checking everything for consistency.
if (!ValidateURLRequestData(*data))
return false;
std::string name_version;
// Allow instance to be 0 or -1 for testing purposes.
if (instance && instance != -1) {
PepperPluginInstanceImpl* instance_impl =
HostGlobals::Get()->GetInstance(instance);
if (instance_impl) {
name_version = MakeXRequestedWithValue(
instance_impl->module()->name(), instance_impl->module()->version());
}
} else {
name_version = "internal_testing_only";
}
dest->SetURL(
frame->GetDocument().CompleteURL(WebString::FromUTF8(data->url)));
dest->SetDownloadToFile(data->stream_to_file);
dest->SetReportUploadProgress(data->record_upload_progress);
if (!data->method.empty())
dest->SetHTTPMethod(WebString::FromUTF8(data->method));
dest->SetSiteForCookies(frame->GetDocument().SiteForCookies());
// Plug-ins should not load via service workers as plug-ins may have their own
// origin checking logic that may get confused if service workers respond with
// resources from another origin.
// https://w3c.github.io/ServiceWorker/#implementer-concerns
dest->SetServiceWorkerMode(WebURLRequest::ServiceWorkerMode::kNone);
const std::string& headers = data->headers;
if (!headers.empty()) {
net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r");
while (it.GetNext()) {
dest->AddHTTPHeaderField(WebString::FromUTF8(it.name()),
WebString::FromUTF8(it.values()));
}
}
// Append the upload data.
if (!data->body.empty()) {
WebHTTPBody http_body;
http_body.Initialize();
int file_index = 0;
for (size_t i = 0; i < data->body.size(); ++i) {
const URLRequestInfoData::BodyItem& item = data->body[i];
if (item.is_file) {
if (!AppendFileRefToBody(instance,
item.file_ref_pp_resource,
item.start_offset,
item.number_of_bytes,
item.expected_last_modified_time,
&http_body))
return false;
file_index++;
} else {
DCHECK(!item.data.empty());
http_body.AppendData(WebData(item.data));
}
}
dest->SetHTTPBody(http_body);
}
// Add the "Referer" header if there is a custom referrer. Such requests
// require universal access. For all other requests, "Referer" will be set
// after header security checks are done in AssociatedURLLoader.
if (data->has_custom_referrer_url && !data->custom_referrer_url.empty())
frame->SetReferrerForRequest(*dest, GURL(data->custom_referrer_url));
if (data->has_custom_content_transfer_encoding &&
!data->custom_content_transfer_encoding.empty()) {
dest->AddHTTPHeaderField(
WebString::FromUTF8("Content-Transfer-Encoding"),
WebString::FromUTF8(data->custom_content_transfer_encoding));
}
if (data->has_custom_user_agent || !name_version.empty()) {
auto extra_data = std::make_unique<RequestExtraData>();
if (data->has_custom_user_agent) {
extra_data->set_custom_user_agent(
WebString::FromUTF8(data->custom_user_agent));
}
if (!name_version.empty()) {
extra_data->set_requested_with(WebString::FromUTF8(name_version));
}
dest->SetExtraData(std::move(extra_data));
}
return true;
}
bool URLRequestRequiresUniversalAccess(const URLRequestInfoData& data) {
return data.has_custom_referrer_url ||
data.has_custom_content_transfer_encoding ||
data.has_custom_user_agent ||
url::FindAndCompareScheme(data.url, url::kJavaScriptScheme, nullptr);
}
} // namespace content