NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / extensions / extension_protocols.cc
blob7d5735a43b925f076f75efd0d0f232c700ea64e2
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.
5 #include "chrome/browser/extensions/extension_protocols.h"
7 #include <algorithm>
9 #include "base/base64.h"
10 #include "base/compiler_specific.h"
11 #include "base/file_util.h"
12 #include "base/files/file_path.h"
13 #include "base/format_macros.h"
14 #include "base/logging.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/path_service.h"
19 #include "base/sha1.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/threading/sequenced_worker_pool.h"
25 #include "base/threading/thread_restrictions.h"
26 #include "base/timer/elapsed_timer.h"
27 #include "build/build_config.h"
28 #include "chrome/browser/extensions/extension_renderer_state.h"
29 #include "chrome/browser/extensions/image_loader.h"
30 #include "chrome/common/chrome_paths.h"
31 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
32 #include "chrome/common/extensions/manifest_url_handler.h"
33 #include "chrome/common/url_constants.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/resource_request_info.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/incognito_info.h"
44 #include "extensions/common/manifest_handlers/shared_module_info.h"
45 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
46 #include "extensions/common/manifest_handlers/webview_info.h"
47 #include "grit/component_extension_resources_map.h"
48 #include "net/base/mime_util.h"
49 #include "net/base/net_errors.h"
50 #include "net/http/http_request_headers.h"
51 #include "net/http/http_response_headers.h"
52 #include "net/http/http_response_info.h"
53 #include "net/url_request/url_request_error_job.h"
54 #include "net/url_request/url_request_file_job.h"
55 #include "net/url_request/url_request_simple_job.h"
56 #include "ui/base/resource/resource_bundle.h"
57 #include "url/url_util.h"
59 using content::BrowserThread;
60 using content::ResourceRequestInfo;
61 using extensions::Extension;
62 using extensions::SharedModuleInfo;
64 namespace {
66 net::HttpResponseHeaders* BuildHttpHeaders(
67 const std::string& content_security_policy, bool send_cors_header,
68 const base::Time& last_modified_time) {
69 std::string raw_headers;
70 raw_headers.append("HTTP/1.1 200 OK");
71 if (!content_security_policy.empty()) {
72 raw_headers.append(1, '\0');
73 raw_headers.append("Content-Security-Policy: ");
74 raw_headers.append(content_security_policy);
77 if (send_cors_header) {
78 raw_headers.append(1, '\0');
79 raw_headers.append("Access-Control-Allow-Origin: *");
82 if (!last_modified_time.is_null()) {
83 // Hash the time and make an etag to avoid exposing the exact
84 // user installation time of the extension.
85 std::string hash = base::StringPrintf("%" PRId64,
86 last_modified_time.ToInternalValue());
87 hash = base::SHA1HashString(hash);
88 std::string etag;
89 base::Base64Encode(hash, &etag);
90 raw_headers.append(1, '\0');
91 raw_headers.append("ETag: \"");
92 raw_headers.append(etag);
93 raw_headers.append("\"");
94 // Also force revalidation.
95 raw_headers.append(1, '\0');
96 raw_headers.append("cache-control: no-cache");
99 raw_headers.append(2, '\0');
100 return new net::HttpResponseHeaders(raw_headers);
103 class URLRequestResourceBundleJob : public net::URLRequestSimpleJob {
104 public:
105 URLRequestResourceBundleJob(net::URLRequest* request,
106 net::NetworkDelegate* network_delegate,
107 const base::FilePath& filename,
108 int resource_id,
109 const std::string& content_security_policy,
110 bool send_cors_header)
111 : net::URLRequestSimpleJob(request, network_delegate),
112 filename_(filename),
113 resource_id_(resource_id),
114 weak_factory_(this) {
115 // Leave cache headers out of resource bundle requests.
116 response_info_.headers = BuildHttpHeaders(content_security_policy,
117 send_cors_header,
118 base::Time());
121 // Overridden from URLRequestSimpleJob:
122 virtual int GetData(std::string* mime_type,
123 std::string* charset,
124 std::string* data,
125 const net::CompletionCallback& callback) const OVERRIDE {
126 const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
127 *data = rb.GetRawDataResource(resource_id_).as_string();
129 // Add the Content-Length header now that we know the resource length.
130 response_info_.headers->AddHeader(base::StringPrintf(
131 "%s: %s", net::HttpRequestHeaders::kContentLength,
132 base::UintToString(data->size()).c_str()));
134 std::string* read_mime_type = new std::string;
135 bool posted = base::PostTaskAndReplyWithResult(
136 BrowserThread::GetBlockingPool(),
137 FROM_HERE,
138 base::Bind(&net::GetMimeTypeFromFile, filename_,
139 base::Unretained(read_mime_type)),
140 base::Bind(&URLRequestResourceBundleJob::OnMimeTypeRead,
141 weak_factory_.GetWeakPtr(),
142 mime_type, charset, data,
143 base::Owned(read_mime_type),
144 callback));
145 DCHECK(posted);
147 return net::ERR_IO_PENDING;
150 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
151 *info = response_info_;
154 private:
155 virtual ~URLRequestResourceBundleJob() { }
157 void OnMimeTypeRead(std::string* out_mime_type,
158 std::string* charset,
159 std::string* data,
160 std::string* read_mime_type,
161 const net::CompletionCallback& callback,
162 bool read_result) {
163 *out_mime_type = *read_mime_type;
164 if (StartsWithASCII(*read_mime_type, "text/", false)) {
165 // All of our HTML files should be UTF-8 and for other resource types
166 // (like images), charset doesn't matter.
167 DCHECK(IsStringUTF8(*data));
168 *charset = "utf-8";
170 int result = read_result ? net::OK : net::ERR_INVALID_URL;
171 callback.Run(result);
174 // We need the filename of the resource to determine the mime type.
175 base::FilePath filename_;
177 // The resource bundle id to load.
178 int resource_id_;
180 net::HttpResponseInfo response_info_;
182 mutable base::WeakPtrFactory<URLRequestResourceBundleJob> weak_factory_;
185 class GeneratedBackgroundPageJob : public net::URLRequestSimpleJob {
186 public:
187 GeneratedBackgroundPageJob(net::URLRequest* request,
188 net::NetworkDelegate* network_delegate,
189 const scoped_refptr<const Extension> extension,
190 const std::string& content_security_policy)
191 : net::URLRequestSimpleJob(request, network_delegate),
192 extension_(extension) {
193 const bool send_cors_headers = false;
194 // Leave cache headers out of generated background page jobs.
195 response_info_.headers = BuildHttpHeaders(content_security_policy,
196 send_cors_headers,
197 base::Time());
200 // Overridden from URLRequestSimpleJob:
201 virtual int GetData(std::string* mime_type,
202 std::string* charset,
203 std::string* data,
204 const net::CompletionCallback& callback) const OVERRIDE {
205 *mime_type = "text/html";
206 *charset = "utf-8";
208 *data = "<!DOCTYPE html>\n<body>\n";
209 const std::vector<std::string>& background_scripts =
210 extensions::BackgroundInfo::GetBackgroundScripts(extension_.get());
211 for (size_t i = 0; i < background_scripts.size(); ++i) {
212 *data += "<script src=\"";
213 *data += background_scripts[i];
214 *data += "\"></script>\n";
217 return net::OK;
220 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
221 *info = response_info_;
224 private:
225 virtual ~GeneratedBackgroundPageJob() {}
227 scoped_refptr<const Extension> extension_;
228 net::HttpResponseInfo response_info_;
231 base::Time GetFileLastModifiedTime(const base::FilePath& filename) {
232 if (base::PathExists(filename)) {
233 base::File::Info info;
234 if (base::GetFileInfo(filename, &info))
235 return info.last_modified;
237 return base::Time();
240 base::Time GetFileCreationTime(const base::FilePath& filename) {
241 if (base::PathExists(filename)) {
242 base::File::Info info;
243 if (base::GetFileInfo(filename, &info))
244 return info.creation_time;
246 return base::Time();
249 void ReadResourceFilePathAndLastModifiedTime(
250 const extensions::ExtensionResource& resource,
251 const base::FilePath& directory,
252 base::FilePath* file_path,
253 base::Time* last_modified_time) {
254 *file_path = resource.GetFilePath();
255 *last_modified_time = GetFileLastModifiedTime(*file_path);
256 // While we're here, log the delta between extension directory
257 // creation time and the resource's last modification time.
258 base::ElapsedTimer query_timer;
259 base::Time dir_creation_time = GetFileCreationTime(directory);
260 UMA_HISTOGRAM_TIMES("Extensions.ResourceDirectoryTimestampQueryLatency",
261 query_timer.Elapsed());
262 int64 delta_seconds = (*last_modified_time - dir_creation_time).InSeconds();
263 if (delta_seconds >= 0) {
264 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedDelta",
265 delta_seconds,
267 base::TimeDelta::FromDays(30).InSeconds(),
268 50);
269 } else {
270 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedNegativeDelta",
271 -delta_seconds,
273 base::TimeDelta::FromDays(30).InSeconds(),
274 50);
278 class URLRequestExtensionJob : public net::URLRequestFileJob {
279 public:
280 URLRequestExtensionJob(net::URLRequest* request,
281 net::NetworkDelegate* network_delegate,
282 const std::string& extension_id,
283 const base::FilePath& directory_path,
284 const base::FilePath& relative_path,
285 const std::string& content_security_policy,
286 bool send_cors_header,
287 bool follow_symlinks_anywhere)
288 : net::URLRequestFileJob(
289 request, network_delegate, base::FilePath(),
290 BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
291 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
292 directory_path_(directory_path),
293 // TODO(tc): Move all of these files into resources.pak so we don't break
294 // when updating on Linux.
295 resource_(extension_id, directory_path, relative_path),
296 content_security_policy_(content_security_policy),
297 send_cors_header_(send_cors_header),
298 weak_factory_(this) {
299 if (follow_symlinks_anywhere) {
300 resource_.set_follow_symlinks_anywhere();
304 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
305 *info = response_info_;
308 virtual void Start() OVERRIDE {
309 base::FilePath* read_file_path = new base::FilePath;
310 base::Time* last_modified_time = new base::Time();
311 bool posted = BrowserThread::PostBlockingPoolTaskAndReply(
312 FROM_HERE,
313 base::Bind(&ReadResourceFilePathAndLastModifiedTime, resource_,
314 directory_path_,
315 base::Unretained(read_file_path),
316 base::Unretained(last_modified_time)),
317 base::Bind(&URLRequestExtensionJob::OnFilePathAndLastModifiedTimeRead,
318 weak_factory_.GetWeakPtr(),
319 base::Owned(read_file_path),
320 base::Owned(last_modified_time)));
321 DCHECK(posted);
324 private:
325 virtual ~URLRequestExtensionJob() {}
327 void OnFilePathAndLastModifiedTimeRead(base::FilePath* read_file_path,
328 base::Time* last_modified_time) {
329 file_path_ = *read_file_path;
330 response_info_.headers = BuildHttpHeaders(
331 content_security_policy_,
332 send_cors_header_,
333 *last_modified_time);
334 URLRequestFileJob::Start();
337 net::HttpResponseInfo response_info_;
338 base::FilePath directory_path_;
339 extensions::ExtensionResource resource_;
340 std::string content_security_policy_;
341 bool send_cors_header_;
342 base::WeakPtrFactory<URLRequestExtensionJob> weak_factory_;
345 bool ExtensionCanLoadInIncognito(const ResourceRequestInfo* info,
346 const std::string& extension_id,
347 extensions::InfoMap* extension_info_map) {
348 if (!extension_info_map->IsIncognitoEnabled(extension_id))
349 return false;
351 // Only allow incognito toplevel navigations to extension resources in
352 // split mode. In spanning mode, the extension must run in a single process,
353 // and an incognito tab prevents that.
354 if (info->GetResourceType() == ResourceType::MAIN_FRAME) {
355 const Extension* extension =
356 extension_info_map->extensions().GetByID(extension_id);
357 return extension && extensions::IncognitoInfo::IsSplitMode(extension);
360 return true;
363 // Returns true if an chrome-extension:// resource should be allowed to load.
364 // TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we
365 // first need to find a way to get CanLoadInIncognito state into the renderers.
366 bool AllowExtensionResourceLoad(net::URLRequest* request,
367 bool is_incognito,
368 const Extension* extension,
369 extensions::InfoMap* extension_info_map) {
370 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
372 // We have seen crashes where info is NULL: crbug.com/52374.
373 if (!info) {
374 LOG(ERROR) << "Allowing load of " << request->url().spec()
375 << "from unknown origin. Could not find user data for "
376 << "request.";
377 return true;
380 if (is_incognito && !ExtensionCanLoadInIncognito(info, request->url().host(),
381 extension_info_map)) {
382 return false;
385 // The following checks are meant to replicate similar set of checks in the
386 // renderer process, performed by ResourceRequestPolicy::CanRequestResource.
387 // These are not exactly equivalent, because we don't have the same bits of
388 // information. The two checks need to be kept in sync as much as possible, as
389 // an exploited renderer can bypass the checks in ResourceRequestPolicy.
391 // Check if the extension for which this request is made is indeed loaded in
392 // the process sending the request. If not, we need to explicitly check if
393 // the resource is explicitly accessible or fits in a set of exception cases.
394 // Note: This allows a case where two extensions execute in the same renderer
395 // process to request each other's resources. We can't do a more precise
396 // check, since the renderer can lie about which extension has made the
397 // request.
398 if (extension_info_map->process_map().Contains(
399 request->url().host(), info->GetChildID())) {
400 return true;
403 // Check workers so that importScripts works from extension workers.
404 if (extension_info_map->worker_process_map().Contains(
405 request->url().host(), info->GetChildID())) {
406 return true;
409 // Extensions with webview: allow loading certain resources by guest renderers
410 // with privileged partition IDs as specified in the manifest file.
411 ExtensionRendererState* renderer_state =
412 ExtensionRendererState::GetInstance();
413 ExtensionRendererState::WebViewInfo webview_info;
414 bool is_guest = renderer_state->GetWebViewInfo(info->GetChildID(),
415 info->GetRouteID(),
416 &webview_info);
417 std::string resource_path = request->url().path();
418 if (is_guest && extensions::WebviewInfo::IsResourceWebviewAccessible(
419 extension, webview_info.partition_id, resource_path)) {
420 return true;
423 // If the request is for navigations outside of webviews, then it should be
424 // allowed. The navigation logic in CrossSiteResourceHandler will properly
425 // transfer the navigation to a privileged process before it commits.
426 if (ResourceType::IsFrame(info->GetResourceType()) && !is_guest)
427 return true;
429 if (!content::PageTransitionIsWebTriggerable(info->GetPageTransition()))
430 return false;
432 // The following checks require that we have an actual extension object. If we
433 // don't have it, allow the request handling to continue with the rest of the
434 // checks.
435 if (!extension)
436 return true;
438 // Disallow loading of packaged resources for hosted apps. We don't allow
439 // hybrid hosted/packaged apps. The one exception is access to icons, since
440 // some extensions want to be able to do things like create their own
441 // launchers.
442 std::string resource_root_relative_path =
443 request->url().path().empty() ? std::string()
444 : request->url().path().substr(1);
445 if (extension->is_hosted_app() &&
446 !extensions::IconsInfo::GetIcons(extension)
447 .ContainsPath(resource_root_relative_path)) {
448 LOG(ERROR) << "Denying load of " << request->url().spec() << " from "
449 << "hosted app.";
450 return false;
453 // Extensions with web_accessible_resources: allow loading by regular
454 // renderers. Since not all subresources are required to be listed in a v2
455 // manifest, we must allow all loads if there are any web accessible
456 // resources. See http://crbug.com/179127.
457 if (extension->manifest_version() < 2 ||
458 extensions::WebAccessibleResourcesInfo::HasWebAccessibleResources(
459 extension)) {
460 return true;
463 // If there aren't any explicitly marked web accessible resources, the
464 // load should be allowed only if it is by DevTools. A close approximation is
465 // checking if the extension contains a DevTools page.
466 if (extensions::ManifestURL::GetDevToolsPage(extension).is_empty())
467 return false;
469 return true;
472 // Returns true if the given URL references an icon in the given extension.
473 bool URLIsForExtensionIcon(const GURL& url, const Extension* extension) {
474 DCHECK(url.SchemeIs(extensions::kExtensionScheme));
476 if (!extension)
477 return false;
479 std::string path = url.path();
480 DCHECK_EQ(url.host(), extension->id());
481 DCHECK(path.length() > 0 && path[0] == '/');
482 path = path.substr(1);
483 return extensions::IconsInfo::GetIcons(extension).ContainsPath(path);
486 class ExtensionProtocolHandler
487 : public net::URLRequestJobFactory::ProtocolHandler {
488 public:
489 ExtensionProtocolHandler(bool is_incognito,
490 extensions::InfoMap* extension_info_map)
491 : is_incognito_(is_incognito), extension_info_map_(extension_info_map) {}
493 virtual ~ExtensionProtocolHandler() {}
495 virtual net::URLRequestJob* MaybeCreateJob(
496 net::URLRequest* request,
497 net::NetworkDelegate* network_delegate) const OVERRIDE;
499 private:
500 const bool is_incognito_;
501 extensions::InfoMap* const extension_info_map_;
502 DISALLOW_COPY_AND_ASSIGN(ExtensionProtocolHandler);
505 // Creates URLRequestJobs for extension:// URLs.
506 net::URLRequestJob*
507 ExtensionProtocolHandler::MaybeCreateJob(
508 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
509 // chrome-extension://extension-id/resource/path.js
510 std::string extension_id = request->url().host();
511 const Extension* extension =
512 extension_info_map_->extensions().GetByID(extension_id);
514 // TODO(mpcomplete): better error code.
515 if (!AllowExtensionResourceLoad(
516 request, is_incognito_, extension, extension_info_map_)) {
517 return new net::URLRequestErrorJob(
518 request, network_delegate, net::ERR_ADDRESS_UNREACHABLE);
521 base::FilePath directory_path;
522 if (extension)
523 directory_path = extension->path();
524 if (directory_path.value().empty()) {
525 const Extension* disabled_extension =
526 extension_info_map_->disabled_extensions().GetByID(extension_id);
527 if (URLIsForExtensionIcon(request->url(), disabled_extension))
528 directory_path = disabled_extension->path();
529 if (directory_path.value().empty()) {
530 LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id;
531 return NULL;
535 std::string content_security_policy;
536 bool send_cors_header = false;
537 bool follow_symlinks_anywhere = false;
538 if (extension) {
539 std::string resource_path = request->url().path();
540 content_security_policy =
541 extensions::CSPInfo::GetResourceContentSecurityPolicy(extension,
542 resource_path);
543 if ((extension->manifest_version() >= 2 ||
544 extensions::WebAccessibleResourcesInfo::HasWebAccessibleResources(
545 extension)) &&
546 extensions::WebAccessibleResourcesInfo::IsResourceWebAccessible(
547 extension, resource_path))
548 send_cors_header = true;
550 follow_symlinks_anywhere =
551 (extension->creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)
552 != 0;
555 std::string path = request->url().path();
556 if (path.size() > 1 &&
557 path.substr(1) == extensions::kGeneratedBackgroundPageFilename) {
558 return new GeneratedBackgroundPageJob(
559 request, network_delegate, extension, content_security_policy);
562 base::FilePath resources_path;
563 base::FilePath relative_path;
564 // Try to load extension resources from chrome resource file if
565 // directory_path is a descendant of resources_path. resources_path
566 // corresponds to src/chrome/browser/resources in source tree.
567 if (PathService::Get(chrome::DIR_RESOURCES, &resources_path) &&
568 // Since component extension resources are included in
569 // component_extension_resources.pak file in resources_path, calculate
570 // extension relative path against resources_path.
571 resources_path.AppendRelativePath(directory_path, &relative_path)) {
572 base::FilePath request_path =
573 extensions::file_util::ExtensionURLToRelativeFilePath(request->url());
574 int resource_id;
575 if (extensions::ImageLoader::IsComponentExtensionResource(
576 directory_path, request_path, &resource_id)) {
577 relative_path = relative_path.Append(request_path);
578 relative_path = relative_path.NormalizePathSeparators();
579 return new URLRequestResourceBundleJob(
580 request,
581 network_delegate,
582 relative_path,
583 resource_id,
584 content_security_policy,
585 send_cors_header);
589 relative_path =
590 extensions::file_util::ExtensionURLToRelativeFilePath(request->url());
592 if (SharedModuleInfo::IsImportedPath(path)) {
593 std::string new_extension_id;
594 std::string new_relative_path;
595 SharedModuleInfo::ParseImportedPath(path, &new_extension_id,
596 &new_relative_path);
597 const Extension* new_extension =
598 extension_info_map_->extensions().GetByID(new_extension_id);
600 bool first_party_in_import = false;
601 // NB: This first_party_for_cookies call is not for security, it is only
602 // used so an exported extension can limit the visible surface to the
603 // extension that imports it, more or less constituting its API.
604 const std::string& first_party_path =
605 request->first_party_for_cookies().path();
606 if (SharedModuleInfo::IsImportedPath(first_party_path)) {
607 std::string first_party_id;
608 std::string dummy;
609 SharedModuleInfo::ParseImportedPath(first_party_path, &first_party_id,
610 &dummy);
611 if (first_party_id == new_extension_id) {
612 first_party_in_import = true;
616 if (SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) &&
617 new_extension &&
618 (first_party_in_import ||
619 SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path))) {
620 directory_path = new_extension->path();
621 extension_id = new_extension_id;
622 relative_path = base::FilePath::FromUTF8Unsafe(new_relative_path);
623 } else {
624 return NULL;
628 return new URLRequestExtensionJob(request,
629 network_delegate,
630 extension_id,
631 directory_path,
632 relative_path,
633 content_security_policy,
634 send_cors_header,
635 follow_symlinks_anywhere);
638 } // namespace
640 net::URLRequestJobFactory::ProtocolHandler* CreateExtensionProtocolHandler(
641 bool is_incognito,
642 extensions::InfoMap* extension_info_map) {
643 return new ExtensionProtocolHandler(is_incognito, extension_info_map);