Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / extensions / browser / extension_protocols.cc
blob53315cb1b92d957b3210d8f69c746d215943935f
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/file_util.h"
14 #include "base/files/file_path.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/path_service.h"
22 #include "base/sha1.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/threading/sequenced_worker_pool.h"
28 #include "base/threading/thread_restrictions.h"
29 #include "base/timer/elapsed_timer.h"
30 #include "build/build_config.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/resource_request_info.h"
33 #include "crypto/secure_hash.h"
34 #include "crypto/sha2.h"
35 #include "extensions/browser/extensions_browser_client.h"
36 #include "extensions/browser/info_map.h"
37 #include "extensions/common/constants.h"
38 #include "extensions/common/extension.h"
39 #include "extensions/common/extension_resource.h"
40 #include "extensions/common/file_util.h"
41 #include "extensions/common/manifest_handlers/background_info.h"
42 #include "extensions/common/manifest_handlers/csp_info.h"
43 #include "extensions/common/manifest_handlers/icons_handler.h"
44 #include "extensions/common/manifest_handlers/incognito_info.h"
45 #include "extensions/common/manifest_handlers/shared_module_info.h"
46 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
47 #include "net/base/io_buffer.h"
48 #include "net/base/net_errors.h"
49 #include "net/http/http_request_headers.h"
50 #include "net/http/http_response_headers.h"
51 #include "net/http/http_response_info.h"
52 #include "net/url_request/url_request_error_job.h"
53 #include "net/url_request/url_request_file_job.h"
54 #include "net/url_request/url_request_simple_job.h"
55 #include "url/url_util.h"
57 using content::BrowserThread;
58 using content::ResourceRequestInfo;
59 using extensions::Extension;
60 using extensions::SharedModuleInfo;
62 namespace extensions {
63 namespace {
65 class GeneratedBackgroundPageJob : public net::URLRequestSimpleJob {
66 public:
67 GeneratedBackgroundPageJob(net::URLRequest* request,
68 net::NetworkDelegate* network_delegate,
69 const scoped_refptr<const Extension> extension,
70 const std::string& content_security_policy)
71 : net::URLRequestSimpleJob(request, network_delegate),
72 extension_(extension) {
73 const bool send_cors_headers = false;
74 // Leave cache headers out of generated background page jobs.
75 response_info_.headers = BuildHttpHeaders(content_security_policy,
76 send_cors_headers,
77 base::Time());
80 // Overridden from URLRequestSimpleJob:
81 virtual int GetData(std::string* mime_type,
82 std::string* charset,
83 std::string* data,
84 const net::CompletionCallback& callback) const OVERRIDE {
85 *mime_type = "text/html";
86 *charset = "utf-8";
88 *data = "<!DOCTYPE html>\n<body>\n";
89 const std::vector<std::string>& background_scripts =
90 extensions::BackgroundInfo::GetBackgroundScripts(extension_.get());
91 for (size_t i = 0; i < background_scripts.size(); ++i) {
92 *data += "<script src=\"";
93 *data += background_scripts[i];
94 *data += "\"></script>\n";
97 return net::OK;
100 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
101 *info = response_info_;
104 private:
105 virtual ~GeneratedBackgroundPageJob() {}
107 scoped_refptr<const Extension> extension_;
108 net::HttpResponseInfo response_info_;
111 base::Time GetFileLastModifiedTime(const base::FilePath& filename) {
112 if (base::PathExists(filename)) {
113 base::File::Info info;
114 if (base::GetFileInfo(filename, &info))
115 return info.last_modified;
117 return base::Time();
120 base::Time GetFileCreationTime(const base::FilePath& filename) {
121 if (base::PathExists(filename)) {
122 base::File::Info info;
123 if (base::GetFileInfo(filename, &info))
124 return info.creation_time;
126 return base::Time();
129 void ReadResourceFilePathAndLastModifiedTime(
130 const extensions::ExtensionResource& resource,
131 const base::FilePath& directory,
132 base::FilePath* file_path,
133 base::Time* last_modified_time) {
134 *file_path = resource.GetFilePath();
135 *last_modified_time = GetFileLastModifiedTime(*file_path);
136 // While we're here, log the delta between extension directory
137 // creation time and the resource's last modification time.
138 base::ElapsedTimer query_timer;
139 base::Time dir_creation_time = GetFileCreationTime(directory);
140 UMA_HISTOGRAM_TIMES("Extensions.ResourceDirectoryTimestampQueryLatency",
141 query_timer.Elapsed());
142 int64 delta_seconds = (*last_modified_time - dir_creation_time).InSeconds();
143 if (delta_seconds >= 0) {
144 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedDelta",
145 delta_seconds,
147 base::TimeDelta::FromDays(30).InSeconds(),
148 50);
149 } else {
150 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedNegativeDelta",
151 -delta_seconds,
153 base::TimeDelta::FromDays(30).InSeconds(),
154 50);
158 class URLRequestExtensionJob : public net::URLRequestFileJob {
159 public:
160 URLRequestExtensionJob(net::URLRequest* request,
161 net::NetworkDelegate* network_delegate,
162 const std::string& extension_id,
163 const base::FilePath& directory_path,
164 const base::FilePath& relative_path,
165 const std::string& content_security_policy,
166 bool send_cors_header,
167 bool follow_symlinks_anywhere)
168 : net::URLRequestFileJob(
169 request,
170 network_delegate,
171 base::FilePath(),
172 BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
173 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
174 seek_position_(0),
175 bytes_read_(0),
176 directory_path_(directory_path),
177 // TODO(tc): Move all of these files into resources.pak so we don't
178 // break when updating on Linux.
179 resource_(extension_id, directory_path, relative_path),
180 content_security_policy_(content_security_policy),
181 send_cors_header_(send_cors_header),
182 weak_factory_(this) {
183 if (follow_symlinks_anywhere) {
184 resource_.set_follow_symlinks_anywhere();
186 const std::string& group =
187 base::FieldTrialList::FindFullName("ExtensionContentHashMeasurement");
188 if (group == "Yes") {
189 base::ElapsedTimer timer;
190 hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
191 hashing_time_ = timer.Elapsed();
195 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
196 *info = response_info_;
199 virtual void Start() OVERRIDE {
200 base::FilePath* read_file_path = new base::FilePath;
201 base::Time* last_modified_time = new base::Time();
202 bool posted = BrowserThread::PostBlockingPoolTaskAndReply(
203 FROM_HERE,
204 base::Bind(&ReadResourceFilePathAndLastModifiedTime,
205 resource_,
206 directory_path_,
207 base::Unretained(read_file_path),
208 base::Unretained(last_modified_time)),
209 base::Bind(&URLRequestExtensionJob::OnFilePathAndLastModifiedTimeRead,
210 weak_factory_.GetWeakPtr(),
211 base::Owned(read_file_path),
212 base::Owned(last_modified_time)));
213 DCHECK(posted);
216 virtual void OnSeekComplete(int64 result) OVERRIDE {
217 DCHECK_EQ(seek_position_, 0);
218 seek_position_ = result;
221 virtual void OnReadComplete(net::IOBuffer* buffer, int result) OVERRIDE {
222 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.OnReadCompleteResult", result);
223 if (result > 0) {
224 bytes_read_ += result;
225 if (hash_.get()) {
226 base::ElapsedTimer timer;
227 hash_->Update(buffer->data(), result);
228 hashing_time_ += timer.Elapsed();
233 private:
234 virtual ~URLRequestExtensionJob() {
235 if (hash_.get()) {
236 base::ElapsedTimer timer;
237 std::string hash_bytes(crypto::kSHA256Length, 0);
238 hash_->Finish(string_as_array(&hash_bytes), hash_bytes.size());
239 hashing_time_ += timer.Elapsed();
240 UMA_HISTOGRAM_TIMES("ExtensionUrlRequest.HashTimeMs", hashing_time_);
242 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.TotalKbRead", bytes_read_ / 1024);
243 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.SeekPosition", seek_position_);
246 void OnFilePathAndLastModifiedTimeRead(base::FilePath* read_file_path,
247 base::Time* last_modified_time) {
248 file_path_ = *read_file_path;
249 response_info_.headers = BuildHttpHeaders(
250 content_security_policy_,
251 send_cors_header_,
252 *last_modified_time);
253 URLRequestFileJob::Start();
256 // A hash of the contents we've read from the file.
257 scoped_ptr<crypto::SecureHash> hash_;
259 // The position we seeked to in the file.
260 int64 seek_position_;
262 // The number of bytes of content we read from the file.
263 int bytes_read_;
265 // Used to count the total time it takes to do hashing operations.
266 base::TimeDelta hashing_time_;
268 net::HttpResponseInfo response_info_;
269 base::FilePath directory_path_;
270 extensions::ExtensionResource resource_;
271 std::string content_security_policy_;
272 bool send_cors_header_;
273 base::WeakPtrFactory<URLRequestExtensionJob> weak_factory_;
276 bool ExtensionCanLoadInIncognito(const ResourceRequestInfo* info,
277 const std::string& extension_id,
278 extensions::InfoMap* extension_info_map) {
279 if (!extension_info_map->IsIncognitoEnabled(extension_id))
280 return false;
282 // Only allow incognito toplevel navigations to extension resources in
283 // split mode. In spanning mode, the extension must run in a single process,
284 // and an incognito tab prevents that.
285 if (info->GetResourceType() == ResourceType::MAIN_FRAME) {
286 const Extension* extension =
287 extension_info_map->extensions().GetByID(extension_id);
288 return extension && extensions::IncognitoInfo::IsSplitMode(extension);
291 return true;
294 // Returns true if an chrome-extension:// resource should be allowed to load.
295 // Pass true for |is_incognito| only for incognito profiles and not Chrome OS
296 // guest mode profiles.
297 // TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we
298 // first need to find a way to get CanLoadInIncognito state into the renderers.
299 bool AllowExtensionResourceLoad(net::URLRequest* request,
300 bool is_incognito,
301 const Extension* extension,
302 extensions::InfoMap* extension_info_map) {
303 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
305 // We have seen crashes where info is NULL: crbug.com/52374.
306 if (!info) {
307 LOG(ERROR) << "Allowing load of " << request->url().spec()
308 << "from unknown origin. Could not find user data for "
309 << "request.";
310 return true;
313 if (is_incognito && !ExtensionCanLoadInIncognito(
314 info, request->url().host(), extension_info_map)) {
315 return false;
318 // The following checks are meant to replicate similar set of checks in the
319 // renderer process, performed by ResourceRequestPolicy::CanRequestResource.
320 // These are not exactly equivalent, because we don't have the same bits of
321 // information. The two checks need to be kept in sync as much as possible, as
322 // an exploited renderer can bypass the checks in ResourceRequestPolicy.
324 // Check if the extension for which this request is made is indeed loaded in
325 // the process sending the request. If not, we need to explicitly check if
326 // the resource is explicitly accessible or fits in a set of exception cases.
327 // Note: This allows a case where two extensions execute in the same renderer
328 // process to request each other's resources. We can't do a more precise
329 // check, since the renderer can lie about which extension has made the
330 // request.
331 if (extension_info_map->process_map().Contains(
332 request->url().host(), info->GetChildID())) {
333 return true;
336 // Allow the extension module embedder to grant permission for loads.
337 if (ExtensionsBrowserClient::Get()->AllowCrossRendererResourceLoad(
338 request, is_incognito, extension, extension_info_map)) {
339 return true;
342 // No special exceptions for cross-process loading. Block the load.
343 return false;
346 // Returns true if the given URL references an icon in the given extension.
347 bool URLIsForExtensionIcon(const GURL& url, const Extension* extension) {
348 DCHECK(url.SchemeIs(extensions::kExtensionScheme));
350 if (!extension)
351 return false;
353 std::string path = url.path();
354 DCHECK_EQ(url.host(), extension->id());
355 DCHECK(path.length() > 0 && path[0] == '/');
356 path = path.substr(1);
357 return extensions::IconsInfo::GetIcons(extension).ContainsPath(path);
360 class ExtensionProtocolHandler
361 : public net::URLRequestJobFactory::ProtocolHandler {
362 public:
363 ExtensionProtocolHandler(bool is_incognito,
364 extensions::InfoMap* extension_info_map)
365 : is_incognito_(is_incognito), extension_info_map_(extension_info_map) {}
367 virtual ~ExtensionProtocolHandler() {}
369 virtual net::URLRequestJob* MaybeCreateJob(
370 net::URLRequest* request,
371 net::NetworkDelegate* network_delegate) const OVERRIDE;
373 private:
374 const bool is_incognito_;
375 extensions::InfoMap* const extension_info_map_;
376 DISALLOW_COPY_AND_ASSIGN(ExtensionProtocolHandler);
379 // Creates URLRequestJobs for extension:// URLs.
380 net::URLRequestJob*
381 ExtensionProtocolHandler::MaybeCreateJob(
382 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
383 // chrome-extension://extension-id/resource/path.js
384 std::string extension_id = request->url().host();
385 const Extension* extension =
386 extension_info_map_->extensions().GetByID(extension_id);
388 // TODO(mpcomplete): better error code.
389 if (!AllowExtensionResourceLoad(
390 request, is_incognito_, extension, extension_info_map_)) {
391 return new net::URLRequestErrorJob(
392 request, network_delegate, net::ERR_ADDRESS_UNREACHABLE);
395 // If this is a disabled extension only allow the icon to load.
396 base::FilePath directory_path;
397 if (extension)
398 directory_path = extension->path();
399 if (directory_path.value().empty()) {
400 const Extension* disabled_extension =
401 extension_info_map_->disabled_extensions().GetByID(extension_id);
402 if (URLIsForExtensionIcon(request->url(), disabled_extension))
403 directory_path = disabled_extension->path();
404 if (directory_path.value().empty()) {
405 LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id;
406 return NULL;
410 // Set up content security policy.
411 std::string content_security_policy;
412 bool send_cors_header = false;
413 bool follow_symlinks_anywhere = false;
414 if (extension) {
415 std::string resource_path = request->url().path();
416 content_security_policy =
417 extensions::CSPInfo::GetResourceContentSecurityPolicy(extension,
418 resource_path);
419 if ((extension->manifest_version() >= 2 ||
420 extensions::WebAccessibleResourcesInfo::HasWebAccessibleResources(
421 extension)) &&
422 extensions::WebAccessibleResourcesInfo::IsResourceWebAccessible(
423 extension, resource_path))
424 send_cors_header = true;
426 follow_symlinks_anywhere =
427 (extension->creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)
428 != 0;
431 // Create a job for a generated background page.
432 std::string path = request->url().path();
433 if (path.size() > 1 &&
434 path.substr(1) == extensions::kGeneratedBackgroundPageFilename) {
435 return new GeneratedBackgroundPageJob(
436 request, network_delegate, extension, content_security_policy);
439 // Component extension resources may be part of the embedder's resource files,
440 // for example component_extension_resources.pak in Chrome.
441 net::URLRequestJob* resource_bundle_job =
442 extensions::ExtensionsBrowserClient::Get()
443 ->MaybeCreateResourceBundleRequestJob(request,
444 network_delegate,
445 directory_path,
446 content_security_policy,
447 send_cors_header);
448 if (resource_bundle_job)
449 return resource_bundle_job;
451 base::FilePath relative_path =
452 extensions::file_util::ExtensionURLToRelativeFilePath(request->url());
454 // Handle shared resources (extension A loading resources out of extension B).
455 if (SharedModuleInfo::IsImportedPath(path)) {
456 std::string new_extension_id;
457 std::string new_relative_path;
458 SharedModuleInfo::ParseImportedPath(path, &new_extension_id,
459 &new_relative_path);
460 const Extension* new_extension =
461 extension_info_map_->extensions().GetByID(new_extension_id);
463 bool first_party_in_import = false;
464 // NB: This first_party_for_cookies call is not for security, it is only
465 // used so an exported extension can limit the visible surface to the
466 // extension that imports it, more or less constituting its API.
467 const std::string& first_party_path =
468 request->first_party_for_cookies().path();
469 if (SharedModuleInfo::IsImportedPath(first_party_path)) {
470 std::string first_party_id;
471 std::string dummy;
472 SharedModuleInfo::ParseImportedPath(first_party_path, &first_party_id,
473 &dummy);
474 if (first_party_id == new_extension_id) {
475 first_party_in_import = true;
479 if (SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) &&
480 new_extension &&
481 (first_party_in_import ||
482 SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path))) {
483 directory_path = new_extension->path();
484 extension_id = new_extension_id;
485 relative_path = base::FilePath::FromUTF8Unsafe(new_relative_path);
486 } else {
487 return NULL;
491 return new URLRequestExtensionJob(request,
492 network_delegate,
493 extension_id,
494 directory_path,
495 relative_path,
496 content_security_policy,
497 send_cors_header,
498 follow_symlinks_anywhere);
501 } // namespace
503 net::HttpResponseHeaders* BuildHttpHeaders(
504 const std::string& content_security_policy,
505 bool send_cors_header,
506 const base::Time& last_modified_time) {
507 std::string raw_headers;
508 raw_headers.append("HTTP/1.1 200 OK");
509 if (!content_security_policy.empty()) {
510 raw_headers.append(1, '\0');
511 raw_headers.append("Content-Security-Policy: ");
512 raw_headers.append(content_security_policy);
515 if (send_cors_header) {
516 raw_headers.append(1, '\0');
517 raw_headers.append("Access-Control-Allow-Origin: *");
520 if (!last_modified_time.is_null()) {
521 // Hash the time and make an etag to avoid exposing the exact
522 // user installation time of the extension.
523 std::string hash =
524 base::StringPrintf("%" PRId64, last_modified_time.ToInternalValue());
525 hash = base::SHA1HashString(hash);
526 std::string etag;
527 base::Base64Encode(hash, &etag);
528 raw_headers.append(1, '\0');
529 raw_headers.append("ETag: \"");
530 raw_headers.append(etag);
531 raw_headers.append("\"");
532 // Also force revalidation.
533 raw_headers.append(1, '\0');
534 raw_headers.append("cache-control: no-cache");
537 raw_headers.append(2, '\0');
538 return new net::HttpResponseHeaders(raw_headers);
541 net::URLRequestJobFactory::ProtocolHandler* CreateExtensionProtocolHandler(
542 bool is_incognito,
543 extensions::InfoMap* extension_info_map) {
544 return new ExtensionProtocolHandler(is_incognito, extension_info_map);
547 } // namespace extensions