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