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/renderer/security_filter_peer.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/strings/stringprintf.h"
9#include "chrome/grit/generated_resources.h"
10#include "net/base/net_errors.h"
11#include "net/http/http_response_headers.h"
12#include "ui/base/l10n/l10n_util.h"
13
14SecurityFilterPeer::SecurityFilterPeer(content::RequestPeer* peer)
15    : original_peer_(peer) {
16}
17
18SecurityFilterPeer::~SecurityFilterPeer() {
19}
20
21// static
22SecurityFilterPeer*
23SecurityFilterPeer::CreateSecurityFilterPeerForDeniedRequest(
24    content::ResourceType resource_type,
25    content::RequestPeer* peer,
26    int os_error) {
27  // Create a filter for SSL and CERT errors.
28  switch (os_error) {
29    case net::ERR_SSL_PROTOCOL_ERROR:
30    case net::ERR_CERT_COMMON_NAME_INVALID:
31    case net::ERR_CERT_DATE_INVALID:
32    case net::ERR_CERT_AUTHORITY_INVALID:
33    case net::ERR_CERT_CONTAINS_ERRORS:
34    case net::ERR_CERT_NO_REVOCATION_MECHANISM:
35    case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
36    case net::ERR_CERT_REVOKED:
37    case net::ERR_CERT_INVALID:
38    case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM:
39    case net::ERR_CERT_WEAK_KEY:
40    case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION:
41    case net::ERR_INSECURE_RESPONSE:
42    case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN:
43      if (content::IsResourceTypeFrame(resource_type))
44        return CreateSecurityFilterPeerForFrame(peer, os_error);
45      // Any other content is entirely filtered-out.
46      return new ReplaceContentPeer(peer, std::string(), std::string());
47    default:
48      // For other errors, we use our normal error handling.
49      return NULL;
50  }
51}
52
53// static
54SecurityFilterPeer* SecurityFilterPeer::CreateSecurityFilterPeerForFrame(
55    content::RequestPeer* peer,
56    int os_error) {
57  // TODO(jcampan): use a different message when getting a phishing/malware
58  // error.
59  std::string html = base::StringPrintf(
60      "<html><meta charset='UTF-8'>"
61      "<body style='background-color:#990000;color:white;'>"
62      "%s</body></html>",
63      l10n_util::GetStringUTF8(IDS_UNSAFE_FRAME_MESSAGE).c_str());
64  return new ReplaceContentPeer(peer, "text/html", html);
65}
66
67void SecurityFilterPeer::OnUploadProgress(uint64 position, uint64 size) {
68  original_peer_->OnUploadProgress(position, size);
69}
70
71bool SecurityFilterPeer::OnReceivedRedirect(
72    const net::RedirectInfo& redirect_info,
73    const content::ResourceResponseInfo& info) {
74  NOTREACHED();
75  return false;
76}
77
78void SecurityFilterPeer::OnReceivedResponse(
79    const content::ResourceResponseInfo& info) {
80  NOTREACHED();
81}
82
83void SecurityFilterPeer::OnReceivedData(const char* data,
84                                        int data_length,
85                                        int encoded_data_length) {
86  NOTREACHED();
87}
88
89void SecurityFilterPeer::OnCompletedRequest(
90    int error_code,
91    bool was_ignored_by_handler,
92    bool stale_copy_in_cache,
93    const std::string& security_info,
94    const base::TimeTicks& completion_time,
95    int64 total_transfer_size) {
96  NOTREACHED();
97}
98
99// static
100void ProcessResponseInfo(const content::ResourceResponseInfo& info_in,
101                         content::ResourceResponseInfo* info_out,
102                         const std::string& mime_type) {
103  DCHECK(info_out);
104  *info_out = info_in;
105  info_out->mime_type = mime_type;
106  // Let's create our own HTTP headers.
107  std::string raw_headers;
108  raw_headers.append("HTTP/1.1 200 OK");
109  raw_headers.push_back('\0');
110  // Don't cache the data we are serving, it is not the real data for that URL
111  // (if the filtered resource were to make it into the WebCore cache, then the
112  // same URL loaded in a safe scenario would still return the filtered
113  // resource).
114  raw_headers.append("cache-control: no-cache");
115  raw_headers.push_back('\0');
116  if (!mime_type.empty()) {
117    raw_headers.append("content-type: ");
118    raw_headers.append(mime_type);
119    raw_headers.push_back('\0');
120  }
121  raw_headers.push_back('\0');
122  net::HttpResponseHeaders* new_headers =
123      new net::HttpResponseHeaders(raw_headers);
124  info_out->headers = new_headers;
125}
126
127////////////////////////////////////////////////////////////////////////////////
128// BufferedPeer
129
130BufferedPeer::BufferedPeer(content::RequestPeer* peer,
131                           const std::string& mime_type)
132    : SecurityFilterPeer(peer), mime_type_(mime_type) {}
133
134BufferedPeer::~BufferedPeer() {
135}
136
137void BufferedPeer::OnReceivedResponse(
138    const content::ResourceResponseInfo& info) {
139  ProcessResponseInfo(info, &response_info_, mime_type_);
140}
141
142void BufferedPeer::OnReceivedData(const char* data,
143                                  int data_length,
144                                  int encoded_data_length) {
145  data_.append(data, data_length);
146}
147
148void BufferedPeer::OnCompletedRequest(int error_code,
149                                      bool was_ignored_by_handler,
150                                      bool stale_copy_in_cache,
151                                      const std::string& security_info,
152                                      const base::TimeTicks& completion_time,
153                                      int64 total_transfer_size) {
154  // Make sure we delete ourselves at the end of this call.
155  scoped_ptr<BufferedPeer> this_deleter(this);
156
157  // Give sub-classes a chance at altering the data.
158  if (error_code != net::OK || !DataReady()) {
159    // Pretend we failed to load the resource.
160    original_peer_->OnReceivedResponse(response_info_);
161    original_peer_->OnCompletedRequest(net::ERR_ABORTED, false,
162                                       stale_copy_in_cache,
163                                       security_info, completion_time,
164                                       total_transfer_size);
165    return;
166  }
167
168  original_peer_->OnReceivedResponse(response_info_);
169  if (!data_.empty())
170    original_peer_->OnReceivedData(data_.data(),
171                                   static_cast<int>(data_.size()),
172                                   -1);
173  original_peer_->OnCompletedRequest(error_code, was_ignored_by_handler,
174                                     stale_copy_in_cache, security_info,
175                                     completion_time, total_transfer_size);
176}
177
178////////////////////////////////////////////////////////////////////////////////
179// ReplaceContentPeer
180
181ReplaceContentPeer::ReplaceContentPeer(content::RequestPeer* peer,
182                                       const std::string& mime_type,
183                                       const std::string& data)
184    : SecurityFilterPeer(peer),
185      mime_type_(mime_type),
186      data_(data) {}
187
188ReplaceContentPeer::~ReplaceContentPeer() {
189}
190
191void ReplaceContentPeer::OnReceivedResponse(
192    const content::ResourceResponseInfo& info) {
193  // Ignore this, we'll serve some alternate content in OnCompletedRequest.
194}
195
196void ReplaceContentPeer::OnReceivedData(const char* data,
197                                        int data_length,
198                                        int encoded_data_length) {
199  // Ignore this, we'll serve some alternate content in OnCompletedRequest.
200}
201
202void ReplaceContentPeer::OnCompletedRequest(
203    int error_code,
204    bool was_ignored_by_handler,
205    bool stale_copy_in_cache,
206    const std::string& security_info,
207    const base::TimeTicks& completion_time,
208    int64 total_transfer_size) {
209  content::ResourceResponseInfo info;
210  ProcessResponseInfo(info, &info, mime_type_);
211  info.security_info = security_info;
212  info.content_length = static_cast<int>(data_.size());
213  original_peer_->OnReceivedResponse(info);
214  if (!data_.empty())
215    original_peer_->OnReceivedData(data_.data(),
216                                   static_cast<int>(data_.size()),
217                                   -1);
218  original_peer_->OnCompletedRequest(net::OK,
219                                     false,
220                                     stale_copy_in_cache,
221                                     security_info,
222                                     completion_time,
223                                     total_transfer_size);
224
225  // The request processing is complete, we must delete ourselves.
226  delete this;
227}
228