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 "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    ResourceType::Type 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 (ResourceType::IsFrame(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 GURL& new_url,
73    const GURL& new_first_party_for_cookies,
74    const content::ResourceResponseInfo& info) {
75  NOTREACHED();
76  return false;
77}
78
79void SecurityFilterPeer::OnReceivedResponse(
80    const content::ResourceResponseInfo& info) {
81  NOTREACHED();
82}
83
84void SecurityFilterPeer::OnReceivedData(const char* data,
85                                        int data_length,
86                                        int encoded_data_length) {
87  NOTREACHED();
88}
89
90void SecurityFilterPeer::OnCompletedRequest(
91    int error_code,
92    bool was_ignored_by_handler,
93    bool stale_copy_in_cache,
94    const std::string& security_info,
95    const base::TimeTicks& completion_time,
96    int64 total_transfer_size) {
97  NOTREACHED();
98}
99
100// static
101void ProcessResponseInfo(const content::ResourceResponseInfo& info_in,
102                         content::ResourceResponseInfo* info_out,
103                         const std::string& mime_type) {
104  DCHECK(info_out);
105  *info_out = info_in;
106  info_out->mime_type = mime_type;
107  // Let's create our own HTTP headers.
108  std::string raw_headers;
109  raw_headers.append("HTTP/1.1 200 OK");
110  raw_headers.push_back('\0');
111  // Don't cache the data we are serving, it is not the real data for that URL
112  // (if the filtered resource were to make it into the WebCore cache, then the
113  // same URL loaded in a safe scenario would still return the filtered
114  // resource).
115  raw_headers.append("cache-control: no-cache");
116  raw_headers.push_back('\0');
117  if (!mime_type.empty()) {
118    raw_headers.append("content-type: ");
119    raw_headers.append(mime_type);
120    raw_headers.push_back('\0');
121  }
122  raw_headers.push_back('\0');
123  net::HttpResponseHeaders* new_headers =
124      new net::HttpResponseHeaders(raw_headers);
125  info_out->headers = new_headers;
126}
127
128////////////////////////////////////////////////////////////////////////////////
129// BufferedPeer
130
131BufferedPeer::BufferedPeer(content::RequestPeer* peer,
132                           const std::string& mime_type)
133    : SecurityFilterPeer(peer), mime_type_(mime_type) {}
134
135BufferedPeer::~BufferedPeer() {
136}
137
138void BufferedPeer::OnReceivedResponse(
139    const content::ResourceResponseInfo& info) {
140  ProcessResponseInfo(info, &response_info_, mime_type_);
141}
142
143void BufferedPeer::OnReceivedData(const char* data,
144                                  int data_length,
145                                  int encoded_data_length) {
146  data_.append(data, data_length);
147}
148
149void BufferedPeer::OnCompletedRequest(int error_code,
150                                      bool was_ignored_by_handler,
151                                      bool stale_copy_in_cache,
152                                      const std::string& security_info,
153                                      const base::TimeTicks& completion_time,
154                                      int64 total_transfer_size) {
155  // Make sure we delete ourselves at the end of this call.
156  scoped_ptr<BufferedPeer> this_deleter(this);
157
158  // Give sub-classes a chance at altering the data.
159  if (error_code != net::OK || !DataReady()) {
160    // Pretend we failed to load the resource.
161    original_peer_->OnReceivedResponse(response_info_);
162    original_peer_->OnCompletedRequest(net::ERR_ABORTED, false,
163                                       stale_copy_in_cache,
164                                       security_info, completion_time,
165                                       total_transfer_size);
166    return;
167  }
168
169  original_peer_->OnReceivedResponse(response_info_);
170  if (!data_.empty())
171    original_peer_->OnReceivedData(data_.data(),
172                                   static_cast<int>(data_.size()),
173                                   -1);
174  original_peer_->OnCompletedRequest(error_code, was_ignored_by_handler,
175                                     stale_copy_in_cache, security_info,
176                                     completion_time, total_transfer_size);
177}
178
179////////////////////////////////////////////////////////////////////////////////
180// ReplaceContentPeer
181
182ReplaceContentPeer::ReplaceContentPeer(content::RequestPeer* peer,
183                                       const std::string& mime_type,
184                                       const std::string& data)
185    : SecurityFilterPeer(peer),
186      mime_type_(mime_type),
187      data_(data) {}
188
189ReplaceContentPeer::~ReplaceContentPeer() {
190}
191
192void ReplaceContentPeer::OnReceivedResponse(
193    const content::ResourceResponseInfo& info) {
194  // Ignore this, we'll serve some alternate content in OnCompletedRequest.
195}
196
197void ReplaceContentPeer::OnReceivedData(const char* data,
198                                        int data_length,
199                                        int encoded_data_length) {
200  // Ignore this, we'll serve some alternate content in OnCompletedRequest.
201}
202
203void ReplaceContentPeer::OnCompletedRequest(
204    int error_code,
205    bool was_ignored_by_handler,
206    bool stale_copy_in_cache,
207    const std::string& security_info,
208    const base::TimeTicks& completion_time,
209    int64 total_transfer_size) {
210  content::ResourceResponseInfo info;
211  ProcessResponseInfo(info, &info, mime_type_);
212  info.security_info = security_info;
213  info.content_length = static_cast<int>(data_.size());
214  original_peer_->OnReceivedResponse(info);
215  if (!data_.empty())
216    original_peer_->OnReceivedData(data_.data(),
217                                   static_cast<int>(data_.size()),
218                                   -1);
219  original_peer_->OnCompletedRequest(net::OK,
220                                     false,
221                                     stale_copy_in_cache,
222                                     security_info,
223                                     completion_time,
224                                     total_transfer_size);
225
226  // The request processing is complete, we must delete ourselves.
227  delete this;
228}
229