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