// 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 #include #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(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()) { RequestExtraData* extra_data = new 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(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