| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/renderer/security_filter_peer.h" |
| |
| #include "base/memory/scoped_ptr.h" |
| #include "base/strings/stringprintf.h" |
| #include "grit/generated_resources.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_response_headers.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| SecurityFilterPeer::SecurityFilterPeer(content::RequestPeer* peer) |
| : original_peer_(peer) { |
| } |
| |
| SecurityFilterPeer::~SecurityFilterPeer() { |
| } |
| |
| // static |
| SecurityFilterPeer* |
| SecurityFilterPeer::CreateSecurityFilterPeerForDeniedRequest( |
| content::ResourceType::Type resource_type, |
| content::RequestPeer* peer, |
| int os_error) { |
| // Create a filter for SSL and CERT errors. |
| switch (os_error) { |
| case net::ERR_SSL_PROTOCOL_ERROR: |
| case net::ERR_CERT_COMMON_NAME_INVALID: |
| case net::ERR_CERT_DATE_INVALID: |
| case net::ERR_CERT_AUTHORITY_INVALID: |
| case net::ERR_CERT_CONTAINS_ERRORS: |
| case net::ERR_CERT_NO_REVOCATION_MECHANISM: |
| case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION: |
| case net::ERR_CERT_REVOKED: |
| case net::ERR_CERT_INVALID: |
| case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM: |
| case net::ERR_CERT_WEAK_KEY: |
| case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION: |
| case net::ERR_INSECURE_RESPONSE: |
| case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: |
| if (content::ResourceType::IsFrame(resource_type)) |
| return CreateSecurityFilterPeerForFrame(peer, os_error); |
| // Any other content is entirely filtered-out. |
| return new ReplaceContentPeer(peer, std::string(), std::string()); |
| default: |
| // For other errors, we use our normal error handling. |
| return NULL; |
| } |
| } |
| |
| // static |
| SecurityFilterPeer* SecurityFilterPeer::CreateSecurityFilterPeerForFrame( |
| content::RequestPeer* peer, |
| int os_error) { |
| // TODO(jcampan): use a different message when getting a phishing/malware |
| // error. |
| std::string html = base::StringPrintf( |
| "<html><meta charset='UTF-8'>" |
| "<body style='background-color:#990000;color:white;'>" |
| "%s</body></html>", |
| l10n_util::GetStringUTF8(IDS_UNSAFE_FRAME_MESSAGE).c_str()); |
| return new ReplaceContentPeer(peer, "text/html", html); |
| } |
| |
| void SecurityFilterPeer::OnUploadProgress(uint64 position, uint64 size) { |
| original_peer_->OnUploadProgress(position, size); |
| } |
| |
| bool SecurityFilterPeer::OnReceivedRedirect( |
| const GURL& new_url, |
| const GURL& new_first_party_for_cookies, |
| const content::ResourceResponseInfo& info) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void SecurityFilterPeer::OnReceivedResponse( |
| const content::ResourceResponseInfo& info) { |
| NOTREACHED(); |
| } |
| |
| void SecurityFilterPeer::OnReceivedData(const char* data, |
| int data_length, |
| int encoded_data_length) { |
| NOTREACHED(); |
| } |
| |
| void SecurityFilterPeer::OnCompletedRequest( |
| int error_code, |
| bool was_ignored_by_handler, |
| bool stale_copy_in_cache, |
| const std::string& security_info, |
| const base::TimeTicks& completion_time, |
| int64 total_transfer_size) { |
| NOTREACHED(); |
| } |
| |
| // static |
| void ProcessResponseInfo(const content::ResourceResponseInfo& info_in, |
| content::ResourceResponseInfo* info_out, |
| const std::string& mime_type) { |
| DCHECK(info_out); |
| *info_out = info_in; |
| info_out->mime_type = mime_type; |
| // Let's create our own HTTP headers. |
| std::string raw_headers; |
| raw_headers.append("HTTP/1.1 200 OK"); |
| raw_headers.push_back('\0'); |
| // Don't cache the data we are serving, it is not the real data for that URL |
| // (if the filtered resource were to make it into the WebCore cache, then the |
| // same URL loaded in a safe scenario would still return the filtered |
| // resource). |
| raw_headers.append("cache-control: no-cache"); |
| raw_headers.push_back('\0'); |
| if (!mime_type.empty()) { |
| raw_headers.append("content-type: "); |
| raw_headers.append(mime_type); |
| raw_headers.push_back('\0'); |
| } |
| raw_headers.push_back('\0'); |
| net::HttpResponseHeaders* new_headers = |
| new net::HttpResponseHeaders(raw_headers); |
| info_out->headers = new_headers; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // BufferedPeer |
| |
| BufferedPeer::BufferedPeer(content::RequestPeer* peer, |
| const std::string& mime_type) |
| : SecurityFilterPeer(peer), mime_type_(mime_type) {} |
| |
| BufferedPeer::~BufferedPeer() { |
| } |
| |
| void BufferedPeer::OnReceivedResponse( |
| const content::ResourceResponseInfo& info) { |
| ProcessResponseInfo(info, &response_info_, mime_type_); |
| } |
| |
| void BufferedPeer::OnReceivedData(const char* data, |
| int data_length, |
| int encoded_data_length) { |
| data_.append(data, data_length); |
| } |
| |
| void BufferedPeer::OnCompletedRequest(int error_code, |
| bool was_ignored_by_handler, |
| bool stale_copy_in_cache, |
| const std::string& security_info, |
| const base::TimeTicks& completion_time, |
| int64 total_transfer_size) { |
| // Make sure we delete ourselves at the end of this call. |
| scoped_ptr<BufferedPeer> this_deleter(this); |
| |
| // Give sub-classes a chance at altering the data. |
| if (error_code != net::OK || !DataReady()) { |
| // Pretend we failed to load the resource. |
| original_peer_->OnReceivedResponse(response_info_); |
| original_peer_->OnCompletedRequest(net::ERR_ABORTED, false, |
| stale_copy_in_cache, |
| security_info, completion_time, |
| total_transfer_size); |
| return; |
| } |
| |
| original_peer_->OnReceivedResponse(response_info_); |
| if (!data_.empty()) |
| original_peer_->OnReceivedData(data_.data(), |
| static_cast<int>(data_.size()), |
| -1); |
| original_peer_->OnCompletedRequest(error_code, was_ignored_by_handler, |
| stale_copy_in_cache, security_info, |
| completion_time, total_transfer_size); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ReplaceContentPeer |
| |
| ReplaceContentPeer::ReplaceContentPeer(content::RequestPeer* peer, |
| const std::string& mime_type, |
| const std::string& data) |
| : SecurityFilterPeer(peer), |
| mime_type_(mime_type), |
| data_(data) {} |
| |
| ReplaceContentPeer::~ReplaceContentPeer() { |
| } |
| |
| void ReplaceContentPeer::OnReceivedResponse( |
| const content::ResourceResponseInfo& info) { |
| // Ignore this, we'll serve some alternate content in OnCompletedRequest. |
| } |
| |
| void ReplaceContentPeer::OnReceivedData(const char* data, |
| int data_length, |
| int encoded_data_length) { |
| // Ignore this, we'll serve some alternate content in OnCompletedRequest. |
| } |
| |
| void ReplaceContentPeer::OnCompletedRequest( |
| int error_code, |
| bool was_ignored_by_handler, |
| bool stale_copy_in_cache, |
| const std::string& security_info, |
| const base::TimeTicks& completion_time, |
| int64 total_transfer_size) { |
| content::ResourceResponseInfo info; |
| ProcessResponseInfo(info, &info, mime_type_); |
| info.security_info = security_info; |
| info.content_length = static_cast<int>(data_.size()); |
| original_peer_->OnReceivedResponse(info); |
| if (!data_.empty()) |
| original_peer_->OnReceivedData(data_.data(), |
| static_cast<int>(data_.size()), |
| -1); |
| original_peer_->OnCompletedRequest(net::OK, |
| false, |
| stale_copy_in_cache, |
| security_info, |
| completion_time, |
| total_transfer_size); |
| |
| // The request processing is complete, we must delete ourselves. |
| delete this; |
| } |