Add testing/scripts/OWNERS
[chromium-blink-merge.git] / extensions / browser / extension_protocols.cc
blob33614956e4540ce504949781b95bdfbcfd30bcb1
1 // Copyright 2014 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.
5 #include "extensions/browser/extension_protocols.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/base64.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/format_macros.h"
16 #include "base/logging.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/metrics/field_trial.h"
20 #include "base/metrics/histogram.h"
21 #include "base/metrics/sparse_histogram.h"
22 #include "base/path_service.h"
23 #include "base/profiler/scoped_tracker.h"
24 #include "base/sha1.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/threading/sequenced_worker_pool.h"
30 #include "base/threading/thread_restrictions.h"
31 #include "base/timer/elapsed_timer.h"
32 #include "build/build_config.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/resource_request_info.h"
35 #include "crypto/secure_hash.h"
36 #include "crypto/sha2.h"
37 #include "extensions/browser/content_verifier.h"
38 #include "extensions/browser/content_verify_job.h"
39 #include "extensions/browser/extensions_browser_client.h"
40 #include "extensions/browser/info_map.h"
41 #include "extensions/browser/url_request_util.h"
42 #include "extensions/common/constants.h"
43 #include "extensions/common/extension.h"
44 #include "extensions/common/extension_resource.h"
45 #include "extensions/common/file_util.h"
46 #include "extensions/common/manifest_handlers/background_info.h"
47 #include "extensions/common/manifest_handlers/csp_info.h"
48 #include "extensions/common/manifest_handlers/icons_handler.h"
49 #include "extensions/common/manifest_handlers/incognito_info.h"
50 #include "extensions/common/manifest_handlers/shared_module_info.h"
51 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
52 #include "net/base/io_buffer.h"
53 #include "net/base/net_errors.h"
54 #include "net/http/http_request_headers.h"
55 #include "net/http/http_response_headers.h"
56 #include "net/http/http_response_info.h"
57 #include "net/url_request/url_request_error_job.h"
58 #include "net/url_request/url_request_file_job.h"
59 #include "net/url_request/url_request_simple_job.h"
60 #include "url/url_util.h"
62 using content::BrowserThread;
63 using content::ResourceRequestInfo;
64 using content::ResourceType;
65 using extensions::Extension;
66 using extensions::SharedModuleInfo;
68 namespace extensions {
69 namespace {
71 class GeneratedBackgroundPageJob : public net::URLRequestSimpleJob {
72 public:
73 GeneratedBackgroundPageJob(net::URLRequest* request,
74 net::NetworkDelegate* network_delegate,
75 const scoped_refptr<const Extension> extension,
76 const std::string& content_security_policy)
77 : net::URLRequestSimpleJob(request, network_delegate),
78 extension_(extension) {
79 const bool send_cors_headers = false;
80 // Leave cache headers out of generated background page jobs.
81 response_info_.headers = BuildHttpHeaders(content_security_policy,
82 send_cors_headers,
83 base::Time());
86 // Overridden from URLRequestSimpleJob:
87 int GetData(std::string* mime_type,
88 std::string* charset,
89 std::string* data,
90 const net::CompletionCallback& callback) const override {
91 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422489 is fixed.
92 tracked_objects::ScopedTracker tracking_profile(
93 FROM_HERE_WITH_EXPLICIT_FUNCTION(
94 "422489 GeneratedBackgroundPageJob::GetData"));
96 *mime_type = "text/html";
97 *charset = "utf-8";
99 *data = "<!DOCTYPE html>\n<body>\n";
100 const std::vector<std::string>& background_scripts =
101 extensions::BackgroundInfo::GetBackgroundScripts(extension_.get());
102 for (size_t i = 0; i < background_scripts.size(); ++i) {
103 *data += "<script src=\"";
104 *data += background_scripts[i];
105 *data += "\"></script>\n";
108 return net::OK;
111 void GetResponseInfo(net::HttpResponseInfo* info) override {
112 *info = response_info_;
115 private:
116 ~GeneratedBackgroundPageJob() override {}
118 scoped_refptr<const Extension> extension_;
119 net::HttpResponseInfo response_info_;
122 base::Time GetFileLastModifiedTime(const base::FilePath& filename) {
123 if (base::PathExists(filename)) {
124 base::File::Info info;
125 if (base::GetFileInfo(filename, &info))
126 return info.last_modified;
128 return base::Time();
131 base::Time GetFileCreationTime(const base::FilePath& filename) {
132 if (base::PathExists(filename)) {
133 base::File::Info info;
134 if (base::GetFileInfo(filename, &info))
135 return info.creation_time;
137 return base::Time();
140 void ReadResourceFilePathAndLastModifiedTime(
141 const extensions::ExtensionResource& resource,
142 const base::FilePath& directory,
143 base::FilePath* file_path,
144 base::Time* last_modified_time) {
145 *file_path = resource.GetFilePath();
146 *last_modified_time = GetFileLastModifiedTime(*file_path);
147 // While we're here, log the delta between extension directory
148 // creation time and the resource's last modification time.
149 base::ElapsedTimer query_timer;
150 base::Time dir_creation_time = GetFileCreationTime(directory);
151 UMA_HISTOGRAM_TIMES("Extensions.ResourceDirectoryTimestampQueryLatency",
152 query_timer.Elapsed());
153 int64 delta_seconds = (*last_modified_time - dir_creation_time).InSeconds();
154 if (delta_seconds >= 0) {
155 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedDelta",
156 delta_seconds,
158 base::TimeDelta::FromDays(30).InSeconds(),
159 50);
160 } else {
161 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedNegativeDelta",
162 -delta_seconds,
164 base::TimeDelta::FromDays(30).InSeconds(),
165 50);
169 class URLRequestExtensionJob : public net::URLRequestFileJob {
170 public:
171 URLRequestExtensionJob(net::URLRequest* request,
172 net::NetworkDelegate* network_delegate,
173 const std::string& extension_id,
174 const base::FilePath& directory_path,
175 const base::FilePath& relative_path,
176 const std::string& content_security_policy,
177 bool send_cors_header,
178 bool follow_symlinks_anywhere,
179 ContentVerifyJob* verify_job)
180 : net::URLRequestFileJob(
181 request,
182 network_delegate,
183 base::FilePath(),
184 BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
185 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
186 verify_job_(verify_job),
187 seek_position_(0),
188 bytes_read_(0),
189 directory_path_(directory_path),
190 // TODO(tc): Move all of these files into resources.pak so we don't
191 // break when updating on Linux.
192 resource_(extension_id, directory_path, relative_path),
193 content_security_policy_(content_security_policy),
194 send_cors_header_(send_cors_header),
195 weak_factory_(this) {
196 if (follow_symlinks_anywhere) {
197 resource_.set_follow_symlinks_anywhere();
201 void GetResponseInfo(net::HttpResponseInfo* info) override {
202 *info = response_info_;
205 void Start() override {
206 request_timer_.reset(new base::ElapsedTimer());
207 base::FilePath* read_file_path = new base::FilePath;
208 base::Time* last_modified_time = new base::Time();
209 bool posted = BrowserThread::PostBlockingPoolTaskAndReply(
210 FROM_HERE,
211 base::Bind(&ReadResourceFilePathAndLastModifiedTime,
212 resource_,
213 directory_path_,
214 base::Unretained(read_file_path),
215 base::Unretained(last_modified_time)),
216 base::Bind(&URLRequestExtensionJob::OnFilePathAndLastModifiedTimeRead,
217 weak_factory_.GetWeakPtr(),
218 base::Owned(read_file_path),
219 base::Owned(last_modified_time)));
220 DCHECK(posted);
223 bool IsRedirectResponse(GURL* location, int* http_status_code) override {
224 return false;
227 void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override {
228 // TODO(asargent) - we'll need to add proper support for range headers.
229 // crbug.com/369895.
230 std::string range_header;
231 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
232 if (verify_job_.get())
233 verify_job_ = NULL;
235 URLRequestFileJob::SetExtraRequestHeaders(headers);
238 void OnSeekComplete(int64 result) override {
239 DCHECK_EQ(seek_position_, 0);
240 seek_position_ = result;
241 // TODO(asargent) - we'll need to add proper support for range headers.
242 // crbug.com/369895.
243 if (result > 0 && verify_job_.get())
244 verify_job_ = NULL;
247 void OnReadComplete(net::IOBuffer* buffer, int result) override {
248 if (result >= 0)
249 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.OnReadCompleteResult", result);
250 else
251 UMA_HISTOGRAM_SPARSE_SLOWLY("ExtensionUrlRequest.OnReadCompleteError",
252 -result);
253 if (result > 0) {
254 bytes_read_ += result;
255 if (verify_job_.get()) {
256 verify_job_->BytesRead(result, buffer->data());
257 if (!remaining_bytes())
258 verify_job_->DoneReading();
263 private:
264 ~URLRequestExtensionJob() override {
265 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.TotalKbRead", bytes_read_ / 1024);
266 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.SeekPosition", seek_position_);
267 if (request_timer_.get())
268 UMA_HISTOGRAM_TIMES("ExtensionUrlRequest.Latency",
269 request_timer_->Elapsed());
272 void OnFilePathAndLastModifiedTimeRead(base::FilePath* read_file_path,
273 base::Time* last_modified_time) {
274 file_path_ = *read_file_path;
275 response_info_.headers = BuildHttpHeaders(
276 content_security_policy_,
277 send_cors_header_,
278 *last_modified_time);
279 URLRequestFileJob::Start();
282 scoped_refptr<ContentVerifyJob> verify_job_;
284 scoped_ptr<base::ElapsedTimer> request_timer_;
286 // The position we seeked to in the file.
287 int64 seek_position_;
289 // The number of bytes of content we read from the file.
290 int bytes_read_;
292 net::HttpResponseInfo response_info_;
293 base::FilePath directory_path_;
294 extensions::ExtensionResource resource_;
295 std::string content_security_policy_;
296 bool send_cors_header_;
297 base::WeakPtrFactory<URLRequestExtensionJob> weak_factory_;
300 bool ExtensionCanLoadInIncognito(const ResourceRequestInfo* info,
301 const std::string& extension_id,
302 extensions::InfoMap* extension_info_map) {
303 if (!extension_info_map->IsIncognitoEnabled(extension_id))
304 return false;
306 // Only allow incognito toplevel navigations to extension resources in
307 // split mode. In spanning mode, the extension must run in a single process,
308 // and an incognito tab prevents that.
309 if (info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
310 const Extension* extension =
311 extension_info_map->extensions().GetByID(extension_id);
312 return extension && extensions::IncognitoInfo::IsSplitMode(extension);
315 return true;
318 // Returns true if an chrome-extension:// resource should be allowed to load.
319 // Pass true for |is_incognito| only for incognito profiles and not Chrome OS
320 // guest mode profiles.
321 // TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we
322 // first need to find a way to get CanLoadInIncognito state into the renderers.
323 bool AllowExtensionResourceLoad(net::URLRequest* request,
324 bool is_incognito,
325 const Extension* extension,
326 extensions::InfoMap* extension_info_map) {
327 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
329 // We have seen crashes where info is NULL: crbug.com/52374.
330 if (!info) {
331 LOG(ERROR) << "Allowing load of " << request->url().spec()
332 << "from unknown origin. Could not find user data for "
333 << "request.";
334 return true;
337 if (is_incognito && !ExtensionCanLoadInIncognito(
338 info, request->url().host(), extension_info_map)) {
339 return false;
342 // The following checks are meant to replicate similar set of checks in the
343 // renderer process, performed by ResourceRequestPolicy::CanRequestResource.
344 // These are not exactly equivalent, because we don't have the same bits of
345 // information. The two checks need to be kept in sync as much as possible, as
346 // an exploited renderer can bypass the checks in ResourceRequestPolicy.
348 // Check if the extension for which this request is made is indeed loaded in
349 // the process sending the request. If not, we need to explicitly check if
350 // the resource is explicitly accessible or fits in a set of exception cases.
351 // Note: This allows a case where two extensions execute in the same renderer
352 // process to request each other's resources. We can't do a more precise
353 // check, since the renderer can lie about which extension has made the
354 // request.
355 if (extension_info_map->process_map().Contains(
356 request->url().host(), info->GetChildID())) {
357 return true;
360 // Allow the extension module embedder to grant permission for loads.
361 if (ExtensionsBrowserClient::Get()->AllowCrossRendererResourceLoad(
362 request, is_incognito, extension, extension_info_map)) {
363 return true;
366 // No special exceptions for cross-process loading. Block the load.
367 return false;
370 // Returns true if the given URL references an icon in the given extension.
371 bool URLIsForExtensionIcon(const GURL& url, const Extension* extension) {
372 DCHECK(url.SchemeIs(extensions::kExtensionScheme));
374 if (!extension)
375 return false;
377 std::string path = url.path();
378 DCHECK_EQ(url.host(), extension->id());
379 DCHECK(path.length() > 0 && path[0] == '/');
380 path = path.substr(1);
381 return extensions::IconsInfo::GetIcons(extension).ContainsPath(path);
384 class ExtensionProtocolHandler
385 : public net::URLRequestJobFactory::ProtocolHandler {
386 public:
387 ExtensionProtocolHandler(bool is_incognito,
388 extensions::InfoMap* extension_info_map)
389 : is_incognito_(is_incognito), extension_info_map_(extension_info_map) {}
391 ~ExtensionProtocolHandler() override {}
393 net::URLRequestJob* MaybeCreateJob(
394 net::URLRequest* request,
395 net::NetworkDelegate* network_delegate) const override;
397 private:
398 const bool is_incognito_;
399 extensions::InfoMap* const extension_info_map_;
400 DISALLOW_COPY_AND_ASSIGN(ExtensionProtocolHandler);
403 // Creates URLRequestJobs for extension:// URLs.
404 net::URLRequestJob*
405 ExtensionProtocolHandler::MaybeCreateJob(
406 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
407 // chrome-extension://extension-id/resource/path.js
408 std::string extension_id = request->url().host();
409 const Extension* extension =
410 extension_info_map_->extensions().GetByID(extension_id);
412 // TODO(mpcomplete): better error code.
413 if (!AllowExtensionResourceLoad(
414 request, is_incognito_, extension, extension_info_map_)) {
415 return new net::URLRequestErrorJob(
416 request, network_delegate, net::ERR_ADDRESS_UNREACHABLE);
419 // If this is a disabled extension only allow the icon to load.
420 base::FilePath directory_path;
421 if (extension)
422 directory_path = extension->path();
423 if (directory_path.value().empty()) {
424 const Extension* disabled_extension =
425 extension_info_map_->disabled_extensions().GetByID(extension_id);
426 if (URLIsForExtensionIcon(request->url(), disabled_extension))
427 directory_path = disabled_extension->path();
428 if (directory_path.value().empty()) {
429 LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id;
430 return NULL;
434 // Set up content security policy.
435 std::string content_security_policy;
436 bool send_cors_header = false;
437 bool follow_symlinks_anywhere = false;
439 if (extension) {
440 std::string resource_path = request->url().path();
442 // Use default CSP for <webview>.
443 if (!url_request_util::IsWebViewRequest(request)) {
444 content_security_policy =
445 extensions::CSPInfo::GetResourceContentSecurityPolicy(extension,
446 resource_path);
449 if ((extension->manifest_version() >= 2 ||
450 extensions::WebAccessibleResourcesInfo::HasWebAccessibleResources(
451 extension)) &&
452 extensions::WebAccessibleResourcesInfo::IsResourceWebAccessible(
453 extension, resource_path)) {
454 send_cors_header = true;
457 follow_symlinks_anywhere =
458 (extension->creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)
459 != 0;
462 // Create a job for a generated background page.
463 std::string path = request->url().path();
464 if (path.size() > 1 &&
465 path.substr(1) == extensions::kGeneratedBackgroundPageFilename) {
466 return new GeneratedBackgroundPageJob(
467 request, network_delegate, extension, content_security_policy);
470 // Component extension resources may be part of the embedder's resource files,
471 // for example component_extension_resources.pak in Chrome.
472 net::URLRequestJob* resource_bundle_job =
473 extensions::ExtensionsBrowserClient::Get()
474 ->MaybeCreateResourceBundleRequestJob(request,
475 network_delegate,
476 directory_path,
477 content_security_policy,
478 send_cors_header);
479 if (resource_bundle_job)
480 return resource_bundle_job;
482 base::FilePath relative_path =
483 extensions::file_util::ExtensionURLToRelativeFilePath(request->url());
485 // Handle shared resources (extension A loading resources out of extension B).
486 if (SharedModuleInfo::IsImportedPath(path)) {
487 std::string new_extension_id;
488 std::string new_relative_path;
489 SharedModuleInfo::ParseImportedPath(path, &new_extension_id,
490 &new_relative_path);
491 const Extension* new_extension =
492 extension_info_map_->extensions().GetByID(new_extension_id);
494 if (SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) &&
495 new_extension) {
496 directory_path = new_extension->path();
497 extension_id = new_extension_id;
498 relative_path = base::FilePath::FromUTF8Unsafe(new_relative_path);
499 } else {
500 return NULL;
503 ContentVerifyJob* verify_job = NULL;
504 ContentVerifier* verifier = extension_info_map_->content_verifier();
505 if (verifier) {
506 verify_job =
507 verifier->CreateJobFor(extension_id, directory_path, relative_path);
508 if (verify_job)
509 verify_job->Start();
512 return new URLRequestExtensionJob(request,
513 network_delegate,
514 extension_id,
515 directory_path,
516 relative_path,
517 content_security_policy,
518 send_cors_header,
519 follow_symlinks_anywhere,
520 verify_job);
523 } // namespace
525 net::HttpResponseHeaders* BuildHttpHeaders(
526 const std::string& content_security_policy,
527 bool send_cors_header,
528 const base::Time& last_modified_time) {
529 std::string raw_headers;
530 raw_headers.append("HTTP/1.1 200 OK");
531 if (!content_security_policy.empty()) {
532 raw_headers.append(1, '\0');
533 raw_headers.append("Content-Security-Policy: ");
534 raw_headers.append(content_security_policy);
537 if (send_cors_header) {
538 raw_headers.append(1, '\0');
539 raw_headers.append("Access-Control-Allow-Origin: *");
542 if (!last_modified_time.is_null()) {
543 // Hash the time and make an etag to avoid exposing the exact
544 // user installation time of the extension.
545 std::string hash =
546 base::StringPrintf("%" PRId64, last_modified_time.ToInternalValue());
547 hash = base::SHA1HashString(hash);
548 std::string etag;
549 base::Base64Encode(hash, &etag);
550 raw_headers.append(1, '\0');
551 raw_headers.append("ETag: \"");
552 raw_headers.append(etag);
553 raw_headers.append("\"");
554 // Also force revalidation.
555 raw_headers.append(1, '\0');
556 raw_headers.append("cache-control: no-cache");
559 raw_headers.append(2, '\0');
560 return new net::HttpResponseHeaders(raw_headers);
563 net::URLRequestJobFactory::ProtocolHandler* CreateExtensionProtocolHandler(
564 bool is_incognito,
565 extensions::InfoMap* extension_info_map) {
566 return new ExtensionProtocolHandler(is_incognito, extension_info_map);
569 } // namespace extensions