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 "content/renderer/pepper/url_request_info_util.h" 6 7 #include "base/logging.h" 8 #include "base/strings/string_util.h" 9 #include "content/child/request_extra_data.h" 10 #include "content/common/fileapi/file_system_messages.h" 11 #include "content/renderer/pepper/host_globals.h" 12 #include "content/renderer/pepper/pepper_file_ref_renderer_host.h" 13 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" 14 #include "content/renderer/pepper/plugin_module.h" 15 #include "content/renderer/pepper/renderer_ppapi_host_impl.h" 16 #include "content/renderer/render_thread_impl.h" 17 #include "net/http/http_util.h" 18 #include "ppapi/c/pp_bool.h" 19 #include "ppapi/c/pp_var.h" 20 #include "ppapi/proxy/ppapi_messages.h" 21 #include "ppapi/shared_impl/url_request_info_data.h" 22 #include "ppapi/shared_impl/var.h" 23 #include "ppapi/thunk/enter.h" 24 #include "third_party/WebKit/public/platform/WebData.h" 25 #include "third_party/WebKit/public/platform/WebHTTPBody.h" 26 #include "third_party/WebKit/public/platform/WebURL.h" 27 #include "third_party/WebKit/public/platform/WebURLRequest.h" 28 #include "third_party/WebKit/public/web/WebDocument.h" 29 #include "third_party/WebKit/public/web/WebFrame.h" 30 #include "url/gurl.h" 31 #include "url/url_util.h" 32 33 using ppapi::Resource; 34 using ppapi::URLRequestInfoData; 35 using ppapi::thunk::EnterResourceNoLock; 36 using blink::WebData; 37 using blink::WebHTTPBody; 38 using blink::WebString; 39 using blink::WebFrame; 40 using blink::WebURL; 41 using blink::WebURLRequest; 42 43 namespace content { 44 45 namespace { 46 47 // Appends the file ref given the Resource pointer associated with it to the 48 // given HTTP body, returning true on success. 49 bool AppendFileRefToBody(PP_Instance instance, 50 PP_Resource resource, 51 int64_t start_offset, 52 int64_t number_of_bytes, 53 PP_Time expected_last_modified_time, 54 WebHTTPBody* http_body) { 55 base::FilePath platform_path; 56 PepperPluginInstanceImpl* instance_impl = 57 HostGlobals::Get()->GetInstance(instance); 58 if (!instance_impl) 59 return false; 60 61 RendererPpapiHost* renderer_ppapi_host = 62 instance_impl->module()->renderer_ppapi_host(); 63 if (!renderer_ppapi_host) 64 return false; 65 ppapi::host::ResourceHost* resource_host = 66 renderer_ppapi_host->GetPpapiHost()->GetResourceHost(resource); 67 if (!resource_host || !resource_host->IsFileRefHost()) 68 return false; 69 PepperFileRefRendererHost* file_ref_host = 70 static_cast<PepperFileRefRendererHost*>(resource_host); 71 switch (file_ref_host->GetFileSystemType()) { 72 case PP_FILESYSTEMTYPE_LOCALTEMPORARY: 73 case PP_FILESYSTEMTYPE_LOCALPERSISTENT: 74 // TODO(kinuko): remove this sync IPC when we fully support 75 // AppendURLRange for FileSystem URL. 76 RenderThreadImpl::current()->Send( 77 new FileSystemHostMsg_SyncGetPlatformPath( 78 file_ref_host->GetFileSystemURL(), &platform_path)); 79 break; 80 case PP_FILESYSTEMTYPE_EXTERNAL: 81 platform_path = file_ref_host->GetExternalFilePath(); 82 break; 83 default: 84 NOTREACHED(); 85 } 86 http_body->appendFileRange(platform_path.AsUTF16Unsafe(), 87 start_offset, 88 number_of_bytes, 89 expected_last_modified_time); 90 return true; 91 } 92 93 // Checks that the request data is valid. Returns false on failure. Note that 94 // method and header validation is done by the URL loader when the request is 95 // opened, and any access errors are returned asynchronously. 96 bool ValidateURLRequestData(const URLRequestInfoData& data) { 97 if (data.prefetch_buffer_lower_threshold < 0 || 98 data.prefetch_buffer_upper_threshold < 0 || 99 data.prefetch_buffer_upper_threshold <= 100 data.prefetch_buffer_lower_threshold) { 101 return false; 102 } 103 return true; 104 } 105 106 std::string FilterStringForXRequestedWithValue(const std::string& s) { 107 std::string rv; 108 rv.reserve(s.length()); 109 for (size_t i = 0; i < s.length(); i++) { 110 char c = s[i]; 111 // Allow ASCII digits, letters, periods, commas, and underscores. (Ignore 112 // all other characters.) 113 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || 114 (c >= 'a' && c <= 'z') || (c == '.') || (c == ',') || (c == '_')) 115 rv.push_back(c); 116 } 117 return rv; 118 } 119 120 // Returns an appropriate value for the X-Requested-With header for plugins that 121 // present an X-Requested-With header. Returns a blank string for other plugins. 122 // We produce a user-agent-like string (eating spaces and other undesired 123 // characters) like "ShockwaveFlash/11.5.31.135" from the plugin name and 124 // version. 125 std::string MakeXRequestedWithValue(const std::string& name, 126 const std::string& version) { 127 std::string rv = FilterStringForXRequestedWithValue(name); 128 if (rv.empty()) 129 return std::string(); 130 131 // Apply to a narrow list of plugins only. 132 if (rv != "ShockwaveFlash" && rv != "PPAPITests") 133 return std::string(); 134 135 std::string filtered_version = FilterStringForXRequestedWithValue(version); 136 if (!filtered_version.empty()) 137 rv += "/" + filtered_version; 138 139 return rv; 140 } 141 142 } // namespace 143 144 bool CreateWebURLRequest(PP_Instance instance, 145 URLRequestInfoData* data, 146 WebFrame* frame, 147 WebURLRequest* dest) { 148 // In the out-of-process case, we've received the URLRequestInfoData 149 // from the untrusted plugin and done no validation on it. We need to be 150 // sure it's not being malicious by checking everything for consistency. 151 if (!ValidateURLRequestData(*data)) 152 return false; 153 154 std::string name_version; 155 156 // Allow instance to be 0 or -1 for testing purposes. 157 if (instance && instance != -1) { 158 PepperPluginInstanceImpl* instance_impl = 159 HostGlobals::Get()->GetInstance(instance); 160 if (instance_impl) { 161 name_version = MakeXRequestedWithValue( 162 instance_impl->module()->name(), 163 instance_impl->module()->version()); 164 } 165 } else { 166 name_version = "internal_testing_only"; 167 } 168 169 dest->initialize(); 170 dest->setURL(frame->document().completeURL(WebString::fromUTF8(data->url))); 171 dest->setDownloadToFile(data->stream_to_file); 172 dest->setReportUploadProgress(data->record_upload_progress); 173 174 if (!data->method.empty()) 175 dest->setHTTPMethod(WebString::fromUTF8(data->method)); 176 177 dest->setFirstPartyForCookies(frame->document().firstPartyForCookies()); 178 179 const std::string& headers = data->headers; 180 if (!headers.empty()) { 181 net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r"); 182 while (it.GetNext()) { 183 dest->addHTTPHeaderField(WebString::fromUTF8(it.name()), 184 WebString::fromUTF8(it.values())); 185 } 186 } 187 188 // Append the upload data. 189 if (!data->body.empty()) { 190 WebHTTPBody http_body; 191 http_body.initialize(); 192 int file_index = 0; 193 for (size_t i = 0; i < data->body.size(); ++i) { 194 const URLRequestInfoData::BodyItem& item = data->body[i]; 195 if (item.is_file) { 196 if (!AppendFileRefToBody(instance, 197 item.file_ref_pp_resource, 198 item.start_offset, 199 item.number_of_bytes, 200 item.expected_last_modified_time, 201 &http_body)) 202 return false; 203 file_index++; 204 } else { 205 DCHECK(!item.data.empty()); 206 http_body.appendData(WebData(item.data)); 207 } 208 } 209 dest->setHTTPBody(http_body); 210 } 211 212 // Add the "Referer" header if there is a custom referrer. Such requests 213 // require universal access. For all other requests, "Referer" will be set 214 // after header security checks are done in AssociatedURLLoader. 215 if (data->has_custom_referrer_url && !data->custom_referrer_url.empty()) 216 frame->setReferrerForRequest(*dest, GURL(data->custom_referrer_url)); 217 218 if (data->has_custom_content_transfer_encoding && 219 !data->custom_content_transfer_encoding.empty()) { 220 dest->addHTTPHeaderField( 221 WebString::fromUTF8("Content-Transfer-Encoding"), 222 WebString::fromUTF8(data->custom_content_transfer_encoding)); 223 } 224 225 if (data->has_custom_user_agent || !name_version.empty()) { 226 RequestExtraData* extra_data = new RequestExtraData(); 227 if (data->has_custom_user_agent) { 228 extra_data->set_custom_user_agent( 229 WebString::fromUTF8(data->custom_user_agent)); 230 } 231 if (!name_version.empty()) { 232 extra_data->set_requested_with(WebString::fromUTF8(name_version)); 233 } 234 dest->setExtraData(extra_data); 235 } 236 237 return true; 238 } 239 240 bool URLRequestRequiresUniversalAccess(const URLRequestInfoData& data) { 241 return data.has_custom_referrer_url || 242 data.has_custom_content_transfer_encoding || 243 data.has_custom_user_agent || 244 url::FindAndCompareScheme(data.url, url::kJavaScriptScheme, NULL); 245 } 246 247 } // namespace content 248