// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Implementation of the SafeBrowsingBlockingPage class. #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h" #include "chrome/app/locales/locale_settings.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_resources.h" #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/google_util.h" #include "chrome/browser/navigation_controller.h" #include "chrome/browser/navigation_entry.h" #include "chrome/browser/tab_util.h" #include "chrome/browser/web_contents.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/l10n_util.h" #include "chrome/common/resource_bundle.h" #include "generated_resources.h" #include "net/base/escape.h" // For malware interstitial pages, we link the problematic URL to Google's // diagnostic page. // TODO(paulg): Change 'googleclient' to a proper client name before launch. static const char* const kSbDiagnosticUrl = "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=googleclient"; static const char* const kSbReportPhishingUrl = "http://www.google.com/safebrowsing/report_error/"; static const wchar_t* const kSbDiagnosticHtml = L"%s"; // Created on the io_thread. SafeBrowsingBlockingPage::SafeBrowsingBlockingPage( SafeBrowsingService* sb_service, SafeBrowsingService::Client* client, int render_process_host_id, int render_view_id, const GURL& url, ResourceType::Type resource_type, SafeBrowsingService::UrlCheckResult result) : sb_service_(sb_service), client_(client), render_process_host_id_(render_process_host_id), render_view_id_(render_view_id), url_(url), result_(result), proceed_(false), tab_(NULL), controller_(NULL), delete_pending_(false), is_main_frame_(resource_type == ResourceType::MAIN_FRAME), created_temporary_entry_(false) { } // Deleted on the io_thread. SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() { } void SafeBrowsingBlockingPage::DisplayBlockingPage() { TabContents* tab = tab_util::GetTabContentsByID(render_process_host_id_, render_view_id_); if (!tab || tab->type() != TAB_CONTENTS_WEB) { NotifyDone(); return; } tab_ = tab; controller_ = tab->controller(); // Register for notifications of events from this tab. NotificationService* ns = NotificationService::current(); DCHECK(ns); ns->AddObserver(this, NOTIFY_TAB_CLOSING, Source(controller_)); ns->AddObserver(this, NOTIFY_DOM_OPERATION_RESPONSE, Source(tab_)); // Hold an extra reference to ourself until the interstitial is gone. AddRef(); WebContents* web_contents = tab->AsWebContents(); // Load the HTML page and create the template components. DictionaryValue strings; ResourceBundle& rb = ResourceBundle::GetSharedInstance(); std::string html; if (result_ == SafeBrowsingService::URL_MALWARE) { std::wstring link = StringPrintf(kSbDiagnosticHtml, l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE).c_str()); strings.SetString(L"badURL", UTF8ToWide(url_.host())); strings.SetString(L"title", l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_TITLE)); strings.SetString(L"headLine", l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_HEADLINE)); // Check to see if we're blocking the main page, or a sub-resource on the // main page. GURL top_url = tab_->GetURL(); if (top_url == url_) { strings.SetString(L"description1", l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION1, UTF8ToWide(url_.host()))); strings.SetString(L"description2", l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION2, link, UTF8ToWide(url_.host()))); } else { strings.SetString(L"description1", l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION4, UTF8ToWide(top_url.host()), UTF8ToWide(url_.host()))); strings.SetString(L"description2", l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION5, link, UTF8ToWide(url_.host()))); } strings.SetString(L"description3", l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION3)); strings.SetString(L"confirm_text", l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION_AGREE)); strings.SetString(L"continue_button", l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_PROCEED_BUTTON)); strings.SetString(L"back_button", l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON)); strings.SetString(L"textdirection", (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? L"rtl" : L"ltr"); html = rb.GetDataResource(IDR_SAFE_BROWSING_MALWARE_BLOCK); } else { strings.SetString(L"title", l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_TITLE)); strings.SetString(L"headLine", l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_HEADLINE)); strings.SetString(L"description1", l10n_util::GetStringF(IDS_SAFE_BROWSING_PHISHING_DESCRIPTION1, UTF8ToWide(url_.host()))); strings.SetString(L"description2", l10n_util::GetStringF(IDS_SAFE_BROWSING_PHISHING_DESCRIPTION2, UTF8ToWide(url_.host()))); strings.SetString(L"continue_button", l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_PROCEED_BUTTON)); strings.SetString(L"back_button", l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_BACK_BUTTON)); strings.SetString(L"report_error", l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_REPORT_ERROR)); strings.SetString(L"textdirection", (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? L"rtl" : L"ltr"); html = rb.GetDataResource(IDR_SAFE_BROWSING_PHISHING_BLOCK); } std::string html_page(jstemplate_builder::GetTemplateHtml(html, &strings, "template_root")); // If the malware is the actual main frame and we have no pending entry // (typically the navigation was initiated by the page), we create a fake // navigation entry (so the location bar shows the page's URL). if (is_main_frame_ && tab_->controller()->GetPendingEntryIndex() == -1) { // New navigation. NavigationEntry* nav_entry = new NavigationEntry(TAB_CONTENTS_WEB); // We set the page ID to max page id so to ensure the controller considers // this dummy entry a new one. Because we'll remove the entry when the // interstitial is going away, it will not conflict with any future // navigations. nav_entry->SetPageID(tab_->GetMaxPageID() + 1); nav_entry->SetPageType(NavigationEntry::INTERSTITIAL_PAGE); nav_entry->SetURL(url_); tab_->controller()->DidNavigateToEntry(nav_entry); created_temporary_entry_ = true; } // Show the interstitial page. web_contents->ShowInterstitialPage(html_page, this); } void SafeBrowsingBlockingPage::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type) { case NOTIFY_TAB_CLOSING: HandleClose(); break; case NOTIFY_DOM_OPERATION_RESPONSE: Continue(Details(details)->json()); break; default: NOTREACHED(); } } void SafeBrowsingBlockingPage::InterstitialClosed() { HandleClose(); } bool SafeBrowsingBlockingPage::GoBack() { WebContents* web_contents = tab_->AsWebContents(); NavigationEntry* prev_entry = web_contents->controller()->GetEntryAtOffset(-1); if (!prev_entry) { // Nothing to go to, default to about:blank. Navigating will cause the // interstitial to hide which will trigger "this" to be deleted. tab_->controller()->LoadURL(GURL("about:blank"), PageTransition::AUTO_BOOKMARK); } else if (prev_entry->GetType() != TAB_CONTENTS_WEB || prev_entry->restored() || !is_main_frame_) { // We do navigate back if any of these is true: // - the page is not a WebContents, its TabContents might have to be // recreated. // - we have not yet visited that navigation entry (typically session // restore), in which case the page is not already available. // - the interstitial was triggered by a sub-resource. In that case we // really need to navigate, just hiding the interstitial would show the // page containing the bad resource, and we don't want that. web_contents->controller()->GoBack(); } else { // Otherwise, the user was viewing a page and navigated to a URL that was // interrupted by an interstitial. Thus, we can just hide the interstitial // and show the page the user was on before. web_contents->HideInterstitialPage(false, false); } // WARNING: at this point we are now either deleted or pending deletion from // the IO thread. // Remove the navigation entry for the malware page. Note that we always // remove the entry even if we did not create it as it has been flagged as // malware and we don't want the user navigating back to it. web_contents->controller()->RemoveLastEntry(); return true; } void SafeBrowsingBlockingPage::Continue(const std::string& user_action) { TabContents* tab = tab_util::GetTabContentsByID(render_process_host_id_, render_view_id_); DCHECK(tab); WebContents* web = tab->AsWebContents(); if (user_action == "2") { // User pressed "Learn more". GURL url; if (result_ == SafeBrowsingService::URL_MALWARE) { url = GURL(l10n_util::GetString(IDS_LEARN_MORE_MALWARE_URL)); } else if (result_ == SafeBrowsingService::URL_PHISHING) { url = GURL(l10n_util::GetString(IDS_LEARN_MORE_PHISHING_URL)); } else { NOTREACHED(); } web->OpenURL(url, CURRENT_TAB, PageTransition::LINK); return; } if (user_action == "3") { // User pressed "Report error" for a phishing site. // Note that we cannot just put a link in the interstitial at this point. // It is not OK to navigate in the context of an interstitial page. DCHECK(result_ == SafeBrowsingService::URL_PHISHING); GURL report_url = safe_browsing_util::GeneratePhishingReportUrl(kSbReportPhishingUrl, url_.spec()); web->OpenURL(report_url, CURRENT_TAB, PageTransition::LINK); return; } if (user_action == "4") { // We're going to take the user to Google's SafeBrowsing diagnostic page. std::string diagnostic = StringPrintf(kSbDiagnosticUrl, EscapeQueryParamValue(url_.spec()).c_str()); GURL diagnostic_url(diagnostic); diagnostic_url = google_util::AppendGoogleLocaleParam(diagnostic_url); DCHECK(result_ == SafeBrowsingService::URL_MALWARE); web->OpenURL(diagnostic_url, CURRENT_TAB, PageTransition::LINK); return; } proceed_ = user_action == "1"; if (proceed_) { // We are continuing, if we have created a temporary navigation entry, // delete it as a new will be created on navigation. if (created_temporary_entry_) web->controller()->RemoveLastEntry(); if (is_main_frame_) web->HideInterstitialPage(true, true); else web->HideInterstitialPage(false, false); } else { GoBack(); } NotifyDone(); } void SafeBrowsingBlockingPage::HandleClose() { NotificationService* ns = NotificationService::current(); DCHECK(ns); ns->RemoveObserver(this, NOTIFY_TAB_CLOSING, Source(controller_)); ns->RemoveObserver(this, NOTIFY_DOM_OPERATION_RESPONSE, Source(tab_)); NotifyDone(); Release(); } void SafeBrowsingBlockingPage::NotifyDone() { if (delete_pending_) return; delete_pending_ = true; if (tab_ && tab_->AsWebContents()) { // Ensure the WebContents does not keep a pointer to us. tab_->AsWebContents()->set_interstitial_delegate(NULL); } Thread* io_thread = g_browser_process->io_thread(); if (!io_thread) return; io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( sb_service_, &SafeBrowsingService::OnBlockingPageDone, this, client_, proceed_)); }