// 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 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::IsResourceTypeFrame(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( "" "" "%s", 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 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(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(data_.size()); original_peer_->OnReceivedResponse(info); if (!data_.empty()) original_peer_->OnReceivedData(data_.data(), static_cast(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; }