summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ssl
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-16 03:29:03 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-16 03:29:03 +0000
commit3b073b258b19937b1dd9b6b2a609dee207865eba (patch)
treefb0779236d58e8a49513c1036d891c2e10c9573c /chrome/browser/ssl
parent7fb087e0800d0faaa31e4a029ad6813f251b4848 (diff)
downloadchromium_src-3b073b258b19937b1dd9b6b2a609dee207865eba.zip
chromium_src-3b073b258b19937b1dd9b6b2a609dee207865eba.tar.gz
chromium_src-3b073b258b19937b1dd9b6b2a609dee207865eba.tar.bz2
Move all the SSL stuff into its own subdir
Review URL: http://codereview.chromium.org/18137 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8165 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ssl')
-rw-r--r--chrome/browser/ssl/ssl_blocking_page.cc140
-rw-r--r--chrome/browser/ssl/ssl_blocking_page.h71
-rw-r--r--chrome/browser/ssl/ssl_error_info.cc250
-rw-r--r--chrome/browser/ssl/ssl_error_info.h83
-rw-r--r--chrome/browser/ssl/ssl_manager.cc725
-rw-r--r--chrome/browser/ssl/ssl_manager.h465
-rw-r--r--chrome/browser/ssl/ssl_policy.cc483
-rw-r--r--chrome/browser/ssl/ssl_policy.h69
-rw-r--r--chrome/browser/ssl/ssl_uitest.cc932
9 files changed, 3218 insertions, 0 deletions
diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc
new file mode 100644
index 0000000..96d0003
--- /dev/null
+++ b/chrome/browser/ssl/ssl_blocking_page.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2006-2008 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/browser/ssl/ssl_blocking_page.h"
+
+#include "base/string_piece.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_resources.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/dom_operation_notification_details.h"
+#include "chrome/browser/ssl/ssl_error_info.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/web_contents.h"
+#include "chrome/common/jstemplate_builder.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+
+#include "generated_resources.h"
+
+// Note that we always create a navigation entry with SSL errors.
+// No error happening loading a sub-resource triggers an interstitial so far.
+SSLBlockingPage::SSLBlockingPage(SSLManager::CertError* error,
+ Delegate* delegate)
+ : InterstitialPage(error->GetWebContents(), true, error->request_url()),
+ error_(error),
+ delegate_(delegate),
+ delegate_has_been_notified_(false) {
+}
+
+SSLBlockingPage::~SSLBlockingPage() {
+ if (!delegate_has_been_notified_) {
+ // The page is closed without the user having chosen what to do, default to
+ // deny.
+ NotifyDenyCertificate();
+ }
+}
+
+std::string SSLBlockingPage::GetHTMLContents() {
+ // Let's build the html error page.
+ DictionaryValue strings;
+ SSLErrorInfo error_info = delegate_->GetSSLErrorInfo(error_);
+ strings.SetString(L"title",
+ l10n_util::GetString(IDS_SSL_BLOCKING_PAGE_TITLE));
+ strings.SetString(L"headLine", error_info.title());
+ strings.SetString(L"description", error_info.details());
+
+ strings.SetString(L"moreInfoTitle",
+ l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_TITLE));
+ SetExtraInfo(&strings, error_info.extra_information());
+
+ strings.SetString(L"proceed",
+ l10n_util::GetString(IDS_SSL_BLOCKING_PAGE_PROCEED));
+ strings.SetString(L"exit",
+ l10n_util::GetString(IDS_SSL_BLOCKING_PAGE_EXIT));
+
+ strings.SetString(L"textdirection",
+ (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ?
+ L"rtl" : L"ltr");
+
+ static const StringPiece html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_SSL_ROAD_BLOCK_HTML));
+
+ return jstemplate_builder::GetTemplateHtml(html, &strings, "template_root");
+}
+
+void SSLBlockingPage::UpdateEntry(NavigationEntry* entry) {
+ DCHECK(tab()->type() == TAB_CONTENTS_WEB);
+ WebContents* web = tab()->AsWebContents();
+ const net::SSLInfo& ssl_info = error_->ssl_info();
+ int cert_id = CertStore::GetSharedInstance()->StoreCert(
+ ssl_info.cert, web->render_view_host()->process()->host_id());
+
+ entry->ssl().set_security_style(SECURITY_STYLE_AUTHENTICATION_BROKEN);
+ entry->ssl().set_cert_id(cert_id);
+ entry->ssl().set_cert_status(ssl_info.cert_status);
+ entry->ssl().set_security_bits(ssl_info.security_bits);
+ NotificationService::current()->Notify(
+ NOTIFY_SSL_STATE_CHANGED,
+ Source<NavigationController>(web->controller()),
+ NotificationService::NoDetails());
+}
+
+void SSLBlockingPage::CommandReceived(const std::string& command) {
+ if (command == "1") {
+ Proceed();
+ } else {
+ DontProceed();
+ }
+}
+
+void SSLBlockingPage::Proceed() {
+ // Accepting the certificate resumes the loading of the page.
+ NotifyAllowCertificate();
+
+ // This call hides and deletes the interstitial.
+ InterstitialPage::Proceed();
+}
+
+void SSLBlockingPage::DontProceed() {
+ NotifyDenyCertificate();
+ InterstitialPage::DontProceed();
+}
+
+
+void SSLBlockingPage::NotifyDenyCertificate() {
+ DCHECK(!delegate_has_been_notified_);
+
+ delegate_->OnDenyCertificate(error_);
+ delegate_has_been_notified_ = true;
+}
+
+void SSLBlockingPage::NotifyAllowCertificate() {
+ DCHECK(!delegate_has_been_notified_);
+
+ delegate_->OnAllowCertificate(error_);
+ delegate_has_been_notified_ = true;
+}
+
+// static
+void SSLBlockingPage::SetExtraInfo(
+ DictionaryValue* strings,
+ const std::vector<std::wstring>& extra_info) {
+ DCHECK(extra_info.size() < 5); // We allow 5 paragraphs max.
+ const std::wstring keys[5] = {
+ L"moreInfo1", L"moreInfo2", L"moreInfo3", L"moreInfo4", L"moreInfo5"
+ };
+ int i;
+ for (i = 0; i < static_cast<int>(extra_info.size()); i++) {
+ strings->SetString(keys[i], extra_info[i]);
+ }
+ for (;i < 5; i++) {
+ strings->SetString(keys[i], L"");
+ }
+}
+
diff --git a/chrome/browser/ssl/ssl_blocking_page.h b/chrome/browser/ssl/ssl_blocking_page.h
new file mode 100644
index 0000000..c3c2289
--- /dev/null
+++ b/chrome/browser/ssl/ssl_blocking_page.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_SSL_BLOCKING_PAGE_H_
+#define CHROME_BROWSER_SSL_BLOCKING_PAGE_H_
+
+#include <string>
+
+#include "chrome/browser/tab_contents/interstitial_page.h"
+#include "chrome/browser/ssl/ssl_manager.h"
+#include "chrome/views/decision.h"
+
+// This class is responsible for showing/hiding the interstitial page that is
+// shown when a certificate error happens.
+// It deletes itself when the interstitial page is closed.
+class SSLBlockingPage : public InterstitialPage {
+ public:
+ // An interface that classes that want to interact with the SSLBlockingPage
+ // should implement.
+ class Delegate {
+ public:
+ // Should return the information about the error that causes this blocking
+ // page.
+ virtual SSLErrorInfo GetSSLErrorInfo(SSLManager::CertError* error) = 0;
+
+ // Notification that the user chose to reject the certificate.
+ virtual void OnDenyCertificate(SSLManager::CertError* error) = 0;
+
+ // Notification that the user chose to accept the certificate.
+ virtual void OnAllowCertificate(SSLManager::CertError* error) = 0;
+ };
+
+ SSLBlockingPage(SSLManager::CertError* error, Delegate* delegate);
+ virtual ~SSLBlockingPage();
+
+ // A method that sets strings in the specified dictionary from the passed
+ // vector so that they can be used to resource the ssl_roadblock.html/
+ // ssl_error.html files.
+ // Note: there can be up to 5 strings in |extra_info|.
+ static void SetExtraInfo(DictionaryValue* strings,
+ const std::vector<std::wstring>& extra_info);
+
+ protected:
+ // InterstitialPage implementation.
+ virtual std::string GetHTMLContents();
+ virtual void CommandReceived(const std::string& command);
+ virtual void UpdateEntry(NavigationEntry* entry);
+ virtual void Proceed();
+ virtual void DontProceed();
+
+ private:
+ void NotifyDenyCertificate();
+ void NotifyAllowCertificate();
+
+ // The error we represent. We will either call CancelRequest() or
+ // ContinueRequest() on this object.
+ scoped_refptr<SSLManager::CertError> error_;
+
+ // Our delegate. It provides useful information, like the title and details
+ // about this error.
+ Delegate* delegate_;
+
+ // A flag to indicate if we've notified |delegate_| of the user's decision.
+ bool delegate_has_been_notified_;
+
+
+ DISALLOW_COPY_AND_ASSIGN(SSLBlockingPage);
+};
+
+#endif // #ifndef CHROME_BROWSER_SSL_BLOCKING_PAGE_H_
diff --git a/chrome/browser/ssl/ssl_error_info.cc b/chrome/browser/ssl/ssl_error_info.cc
new file mode 100644
index 0000000..ee12ae1
--- /dev/null
+++ b/chrome/browser/ssl/ssl_error_info.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2006-2008 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/browser/ssl/ssl_error_info.h"
+
+#include "base/string_util.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/time_format.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/ssl_info.h"
+#include "googleurl/src/gurl.h"
+
+#include "chromium_strings.h"
+#include "generated_resources.h"
+
+SSLErrorInfo::SSLErrorInfo(const std::wstring& title,
+ const std::wstring& details,
+ const std::wstring& short_description,
+ const std::vector<std::wstring>& extra_info)
+ : title_(title),
+ details_(details),
+ short_description_(short_description),
+ extra_information_(extra_info) {
+}
+
+// static
+SSLErrorInfo SSLErrorInfo::CreateError(ErrorType error_type,
+ net::X509Certificate* cert,
+ const GURL& request_url) {
+ std::wstring title, details, short_description;
+ std::vector<std::wstring> extra_info;
+ switch (error_type) {
+ case CERT_COMMON_NAME_INVALID: {
+ title = l10n_util::GetString(IDS_CERT_ERROR_COMMON_NAME_INVALID_TITLE);
+ // If the certificate contains multiple DNS names, we choose the most
+ // representative one -- either the DNS name that's also in the subject
+ // field, or the first one. If this heuristic turns out to be
+ // inadequate, we can consider choosing the DNS name that is the
+ // "closest match" to the host name in the request URL, or listing all
+ // the DNS names with an HTML <ul>.
+ std::vector<std::string> dns_names;
+ cert->GetDNSNames(&dns_names);
+ DCHECK(!dns_names.empty());
+ size_t i = 0;
+ for (; i < dns_names.size(); ++i) {
+ if (dns_names[i] == cert->subject().common_name)
+ break;
+ }
+ if (i == dns_names.size())
+ i = 0;
+ details =
+ l10n_util::GetStringF(IDS_CERT_ERROR_COMMON_NAME_INVALID_DETAILS,
+ UTF8ToWide(request_url.host()),
+ UTF8ToWide(dns_names[i]),
+ UTF8ToWide(request_url.host()));
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_COMMON_NAME_INVALID_DESCRIPTION);
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_1));
+ extra_info.push_back(
+ l10n_util::GetStringF(
+ IDS_CERT_ERROR_COMMON_NAME_INVALID_EXTRA_INFO_2,
+ UTF8ToWide(cert->subject().common_name),
+ UTF8ToWide(request_url.host())));
+ break;
+ }
+ case CERT_DATE_INVALID:
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_1));
+ if (cert->HasExpired()) {
+ title = l10n_util::GetString(IDS_CERT_ERROR_EXPIRED_TITLE);
+ details = l10n_util::GetStringF(IDS_CERT_ERROR_EXPIRED_DETAILS,
+ UTF8ToWide(request_url.host()),
+ UTF8ToWide(request_url.host()));
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_EXPIRED_DESCRIPTION);
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_EXPIRED_DETAILS_EXTRA_INFO_2));
+ } else {
+ // Then it must be not yet valid. We don't check that it is not yet
+ // valid as there is still a very unlikely chance that the cert might
+ // have become valid since the error occurred.
+ title = l10n_util::GetString(IDS_CERT_ERROR_NOT_YET_VALID_TITLE);
+ details = l10n_util::GetStringF(IDS_CERT_ERROR_NOT_YET_VALID_DETAILS,
+ UTF8ToWide(request_url.host()),
+ UTF8ToWide(request_url.host()));
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_NOT_YET_VALID_DESCRIPTION);
+ extra_info.push_back(
+ l10n_util::GetString(
+ IDS_CERT_ERROR_NOT_YET_VALID_DETAILS_EXTRA_INFO_2));
+ }
+ break;
+ case CERT_AUTHORITY_INVALID:
+ title = l10n_util::GetString(IDS_CERT_ERROR_AUTHORITY_INVALID_TITLE);
+ details = l10n_util::GetStringF(IDS_CERT_ERROR_AUTHORITY_INVALID_DETAILS,
+ UTF8ToWide(request_url.host()));
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_AUTHORITY_INVALID_DESCRIPTION);
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_1));
+ extra_info.push_back(
+ l10n_util::GetStringF(IDS_CERT_ERROR_AUTHORITY_INVALID_EXTRA_INFO_2,
+ UTF8ToWide(request_url.host()),
+ UTF8ToWide(request_url.host())));
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_AUTHORITY_INVALID_EXTRA_INFO_3));
+ break;
+ case CERT_CONTAINS_ERRORS:
+ title = l10n_util::GetString(IDS_CERT_ERROR_CONTAINS_ERRORS_TITLE);
+ details = l10n_util::GetStringF(IDS_CERT_ERROR_CONTAINS_ERRORS_DETAILS,
+ UTF8ToWide(request_url.host()));
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_CONTAINS_ERRORS_DESCRIPTION);
+ extra_info.push_back(
+ l10n_util::GetStringF(IDS_CERT_ERROR_EXTRA_INFO_1,
+ UTF8ToWide(request_url.host())));
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_CONTAINS_ERRORS_EXTRA_INFO_2));
+ break;
+ case CERT_NO_REVOCATION_MECHANISM:
+ title =
+ l10n_util::GetString(IDS_CERT_ERROR_NO_REVOCATION_MECHANISM_TITLE);
+ details =
+ l10n_util::GetString(IDS_CERT_ERROR_NO_REVOCATION_MECHANISM_DETAILS);
+ short_description = l10n_util::GetString(
+ IDS_CERT_ERROR_NO_REVOCATION_MECHANISM_DESCRIPTION);
+ break;
+ case CERT_UNABLE_TO_CHECK_REVOCATION:
+ title =
+ l10n_util::GetString(IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_TITLE);
+ details = l10n_util::GetString(
+ IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_DETAILS);
+ short_description = l10n_util::GetString(
+ IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_DESCRIPTION);
+ break;
+ case CERT_REVOKED:
+ title = l10n_util::GetString(IDS_CERT_ERROR_REVOKED_CERT_TITLE);
+ details = l10n_util::GetStringF(IDS_CERT_ERROR_REVOKED_CERT_DETAILS,
+ UTF8ToWide(request_url.host()));
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_REVOKED_CERT_DESCRIPTION);
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_1));
+ extra_info.push_back(
+ l10n_util::GetString(IDS_CERT_ERROR_REVOKED_CERT_EXTRA_INFO_2));
+ break;
+ case CERT_INVALID:
+ title = l10n_util::GetString(IDS_CERT_ERROR_INVALID_CERT_TITLE);
+ details = l10n_util::GetString(IDS_CERT_ERROR_INVALID_CERT_DETAILS);
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_INVALID_CERT_DESCRIPTION);
+ break;
+ case MIXED_CONTENTS:
+ title = l10n_util::GetString(IDS_SSL_MIXED_CONTENT_TITLE);
+ details = l10n_util::GetString(IDS_SSL_MIXED_CONTENT_DETAILS);
+ short_description =
+ l10n_util::GetString(IDS_SSL_MIXED_CONTENT_DESCRIPTION);
+ break;
+ case UNSAFE_CONTENTS:
+ title = l10n_util::GetString(IDS_SSL_UNSAFE_CONTENT_TITLE);
+ details = l10n_util::GetString(IDS_SSL_UNSAFE_CONTENT_DETAILS);
+ short_description =
+ l10n_util::GetString(IDS_SSL_UNSAFE_CONTENT_DESCRIPTION);
+ break;
+ case UNKNOWN:
+ title = l10n_util::GetString(IDS_CERT_ERROR_UNKNOWN_ERROR_TITLE);
+ details = l10n_util::GetString(IDS_CERT_ERROR_UNKNOWN_ERROR_DETAILS);
+ short_description =
+ l10n_util::GetString(IDS_CERT_ERROR_UNKNOWN_ERROR_DESCRIPTION);
+ break;
+ default:
+ NOTREACHED();
+ }
+ return SSLErrorInfo(title, details, short_description, extra_info);
+}
+
+SSLErrorInfo::~SSLErrorInfo() {
+}
+
+// static
+SSLErrorInfo::ErrorType SSLErrorInfo::NetErrorToErrorType(int net_error) {
+ switch (net_error) {
+ case net::ERR_CERT_COMMON_NAME_INVALID:
+ return CERT_COMMON_NAME_INVALID;
+ case net::ERR_CERT_DATE_INVALID:
+ return CERT_DATE_INVALID;
+ case net::ERR_CERT_AUTHORITY_INVALID:
+ return CERT_AUTHORITY_INVALID;
+ case net::ERR_CERT_CONTAINS_ERRORS:
+ return CERT_CONTAINS_ERRORS;
+ case net::ERR_CERT_NO_REVOCATION_MECHANISM:
+ return CERT_NO_REVOCATION_MECHANISM;
+ case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
+ return CERT_UNABLE_TO_CHECK_REVOCATION;
+ case net::ERR_CERT_REVOKED:
+ return CERT_REVOKED;
+ case net::ERR_CERT_INVALID:
+ return CERT_INVALID;
+ default:
+ NOTREACHED();
+ return UNKNOWN;
+ }
+}
+
+// static
+int SSLErrorInfo::GetErrorsForCertStatus(int cert_id,
+ int cert_status,
+ const GURL& url,
+ std::vector<SSLErrorInfo>* errors) {
+ const int kErrorFlags[] = {
+ net::CERT_STATUS_COMMON_NAME_INVALID,
+ net::CERT_STATUS_DATE_INVALID,
+ net::CERT_STATUS_AUTHORITY_INVALID,
+ net::CERT_STATUS_NO_REVOCATION_MECHANISM,
+ net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION,
+ net::CERT_STATUS_REVOKED,
+ net::CERT_STATUS_INVALID
+ };
+
+ const ErrorType kErrorTypes[] = {
+ CERT_COMMON_NAME_INVALID,
+ CERT_DATE_INVALID,
+ CERT_AUTHORITY_INVALID,
+ CERT_NO_REVOCATION_MECHANISM,
+ CERT_UNABLE_TO_CHECK_REVOCATION,
+ CERT_REVOKED,
+ CERT_INVALID
+ };
+ DCHECK(arraysize(kErrorFlags) == arraysize(kErrorTypes));
+
+ scoped_refptr<net::X509Certificate> cert = NULL;
+ int count = 0;
+ for (size_t i = 0; i < arraysize(kErrorFlags); ++i) {
+ if (cert_status & kErrorFlags[i]) {
+ count++;
+ if (!cert.get()) {
+ bool r = CertStore::GetSharedInstance()->RetrieveCert(cert_id, &cert);
+ DCHECK(r);
+ }
+ if (errors)
+ errors->push_back(SSLErrorInfo::CreateError(kErrorTypes[i], cert, url));
+ }
+ }
+ return count;
+}
+
diff --git a/chrome/browser/ssl/ssl_error_info.h b/chrome/browser/ssl/ssl_error_info.h
new file mode 100644
index 0000000..c7d6698
--- /dev/null
+++ b/chrome/browser/ssl/ssl_error_info.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_SSL_ERROR_INFO_H__
+#define CHROME_BROWSER_SSL_ERROR_INFO_H__
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "net/base/x509_certificate.h"
+
+class GURL;
+
+// This class describes an error that happened while showing a page over SSL.
+// An SSLErrorInfo object only exists on the UI thread and only contains
+// information about an error (type of error and text details).
+// Note no DISALLOW_EVIL_CONSTRUCTORS as we want the copy constructor.
+class SSLErrorInfo {
+ public:
+ enum ErrorType {
+ CERT_COMMON_NAME_INVALID = 0,
+ CERT_DATE_INVALID,
+ CERT_AUTHORITY_INVALID,
+ CERT_CONTAINS_ERRORS,
+ CERT_NO_REVOCATION_MECHANISM,
+ CERT_UNABLE_TO_CHECK_REVOCATION,
+ CERT_REVOKED,
+ CERT_INVALID,
+ MIXED_CONTENTS,
+ UNSAFE_CONTENTS,
+ UNKNOWN
+ };
+
+ virtual ~SSLErrorInfo();
+
+ // Converts a network error code to an ErrorType.
+ static ErrorType NetErrorToErrorType(int net_error);
+
+ static SSLErrorInfo CreateError(ErrorType error_type,
+ net::X509Certificate* cert,
+ const GURL& request_url);
+
+ // Populates the specified |errors| vector with the errors contained in
+ // |cert_status|. Returns the number of errors found.
+ // Callers only interested in the error count can pass NULL for |errors|.
+ static int GetErrorsForCertStatus(int cert_status,
+ int cert_id,
+ const GURL& request_url,
+ std::vector<SSLErrorInfo>* errors);
+
+ // A title describing the error, usually to be used with the details below.
+ const std::wstring& title() const { return title_; }
+
+ // A description of the error.
+ const std::wstring& details() const { return details_; }
+
+ // A short message describing the error (1 line).
+ const std::wstring& short_description() const { return short_description_; }
+
+ // A lengthy explanation of what the error is. Each entry in the returned
+ // vector is a paragraph.
+ const std::vector<std::wstring>& extra_information() const {
+ return extra_information_;
+ }
+
+private:
+ SSLErrorInfo(const std::wstring& title,
+ const std::wstring& details,
+ const std::wstring& short_description,
+ const std::vector<std::wstring>& extra_info);
+
+ std::wstring title_;
+ std::wstring details_;
+ std::wstring short_description_;
+ // Extra-informations contains paragraphs of text explaining in details what
+ // the error is and what the risks are.
+ std::vector<std::wstring> extra_information_;
+};
+
+#endif // CHROME_BROWSER_SSL_ERROR_INFO_H__
+
diff --git a/chrome/browser/ssl/ssl_manager.cc b/chrome/browser/ssl/ssl_manager.cc
new file mode 100644
index 0000000..b2fd246
--- /dev/null
+++ b/chrome/browser/ssl/ssl_manager.cc
@@ -0,0 +1,725 @@
+// Copyright (c) 2006-2008 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/browser/ssl/ssl_manager.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/load_notification_details.h"
+#include "chrome/browser/load_from_memory_cache_details.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/resource_request_details.h"
+#include "chrome/browser/ssl/ssl_error_info.h"
+#include "chrome/browser/ssl/ssl_policy.h"
+#include "chrome/browser/tab_contents/infobar_delegate.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/provisional_load_details.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/tab_contents/web_contents.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/decision.h"
+#include "chrome/views/link.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request.h"
+#include "webkit/glue/resource_type.h"
+#include "generated_resources.h"
+
+class SSLInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+ SSLInfoBarDelegate(TabContents* contents,
+ const std::wstring message,
+ const std::wstring& button_label,
+ Task* task)
+ : ConfirmInfoBarDelegate(contents),
+ message_(message),
+ button_label_(button_label),
+ task_(task) {
+ }
+ virtual ~SSLInfoBarDelegate() {}
+
+ // Overridden from ConfirmInfoBarDelegate:
+ virtual void InfoBarClosed() {
+ delete this;
+ }
+ virtual std::wstring GetMessageText() const {
+ return message_;
+ }
+ virtual SkBitmap* GetIcon() const {
+ return ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_INFOBAR_SSL_WARNING);
+ }
+ virtual int GetButtons() const {
+ return !button_label_.empty() ? BUTTON_OK : BUTTON_NONE;
+ }
+ virtual std::wstring GetButtonLabel(InfoBarButton button) const {
+ return button_label_;
+ }
+ virtual bool Accept() {
+ if (task_.get()) {
+ task_->Run();
+ task_.reset(); // Ensures we won't run the task again.
+ }
+ return true;
+ }
+
+ private:
+ // Labels for the InfoBar's message and button.
+ std::wstring message_;
+ std::wstring button_label_;
+
+ // A task to run when the InfoBar is accepted.
+ scoped_ptr<Task> task_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLInfoBarDelegate);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// SSLManager
+
+// static
+void SSLManager::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterIntegerPref(prefs::kMixedContentFiltering,
+ FilterPolicy::DONT_FILTER);
+}
+
+SSLManager::SSLManager(NavigationController* controller, Delegate* delegate)
+ : controller_(controller),
+ delegate_(delegate) {
+ DCHECK(controller_);
+
+ // If do delegate is supplied, use the default policy.
+ if (!delegate_)
+ delegate_ = SSLPolicy::GetDefaultPolicy();
+
+ // Subscribe to various notifications.
+ registrar_.Add(this, NOTIFY_NAV_ENTRY_COMMITTED,
+ Source<NavigationController>(controller_));
+ registrar_.Add(this, NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR,
+ Source<NavigationController>(controller_));
+ registrar_.Add(this, NOTIFY_RESOURCE_RESPONSE_STARTED,
+ Source<NavigationController>(controller_));
+ registrar_.Add(this, NOTIFY_RESOURCE_RECEIVED_REDIRECT,
+ Source<NavigationController>(controller_));
+ registrar_.Add(this, NOTIFY_LOAD_FROM_MEMORY_CACHE,
+ Source<NavigationController>(controller_));
+}
+
+SSLManager::~SSLManager() {
+}
+
+// Delegate API method.
+void SSLManager::ShowMessage(const std::wstring& msg) {
+ ShowMessageWithLink(msg, std::wstring(), NULL);
+}
+
+void SSLManager::ShowMessageWithLink(const std::wstring& msg,
+ const std::wstring& link_text,
+ Task* task) {
+ if (controller_->GetPendingEntry()) {
+ // The main frame is currently loading, wait until the load is committed so
+ // to show the error on the right page (once the location bar shows the
+ // correct url).
+ if (std::find(pending_messages_.begin(), pending_messages_.end(), msg) ==
+ pending_messages_.end())
+ pending_messages_.push_back(SSLMessageInfo(msg, link_text, task));
+
+ return;
+ }
+
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ if (!entry)
+ return;
+
+ // Don't show the message if the user doesn't expect an authenticated session.
+ if (entry->ssl().security_style() <= SECURITY_STYLE_UNAUTHENTICATED)
+ return;
+
+ if (controller_->active_contents()) {
+ controller_->active_contents()->AddInfoBar(
+ new SSLInfoBarDelegate(controller_->active_contents(), msg, link_text,
+ task));
+ }
+}
+
+// Delegate API method.
+bool SSLManager::SetMaxSecurityStyle(SecurityStyle style) {
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ if (!entry) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (entry->ssl().security_style() > style) {
+ entry->ssl().set_security_style(style);
+ return true;
+ }
+ return false;
+}
+
+// Delegate API method.
+void SSLManager::AddMessageToConsole(const std::wstring& msg,
+ ConsoleMessageLevel level) {
+ TabContents* tab_contents = controller_->GetTabContents(TAB_CONTENTS_WEB);
+ if (!tab_contents)
+ return;
+ WebContents* web_contents = tab_contents->AsWebContents();
+ if (!web_contents)
+ return;
+
+ web_contents->render_view_host()->AddMessageToConsole(
+ std::wstring(), msg, level);
+}
+
+// Delegate API method.
+void SSLManager::DenyCertForHost(net::X509Certificate* cert,
+ const std::string& host) {
+ // Remember that we don't like this cert for this host.
+ // TODO(abarth): Do we want to persist this information in the user's profile?
+ cert_policy_for_host_[host].Deny(cert);
+}
+
+// Delegate API method.
+void SSLManager::AllowCertForHost(net::X509Certificate* cert,
+ const std::string& host) {
+ // Remember that we do like this cert for this host.
+ // TODO(abarth): Do we want to persist this information in the user's profile?
+ cert_policy_for_host_[host].Allow(cert);
+}
+
+// Delegate API method.
+net::X509Certificate::Policy::Judgment SSLManager::QueryPolicy(
+ net::X509Certificate* cert, const std::string& host) {
+ // TODO(abarth): Do we want to read this information from the user's profile?
+ return cert_policy_for_host_[host].Check(cert);
+}
+
+bool SSLManager::CanShowInsecureContent(const GURL& url) {
+ // TODO(jcampan): Do we want to read this information from the user's profile?
+ return (can_show_insecure_content_for_host_.find(url.host()) !=
+ can_show_insecure_content_for_host_.end());
+}
+
+void SSLManager::AllowShowInsecureContentForURL(const GURL& url) {
+ can_show_insecure_content_for_host_.insert(url.host());
+}
+
+bool SSLManager::ProcessedSSLErrorFromRequest() const {
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ if (!entry) {
+ NOTREACHED();
+ return false;
+ }
+
+ return net::IsCertStatusError(entry->ssl().cert_status());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ErrorHandler
+
+SSLManager::ErrorHandler::ErrorHandler(ResourceDispatcherHost* rdh,
+ URLRequest* request,
+ MessageLoop* ui_loop)
+ : ui_loop_(ui_loop),
+ io_loop_(MessageLoop::current()),
+ manager_(NULL),
+ resource_dispatcher_host_(rdh),
+ request_has_been_notified_(false),
+ request_id_(0, 0),
+ request_url_(request->url()) {
+ DCHECK(MessageLoop::current() != ui_loop);
+
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+ request_id_.render_process_host_id = info->render_process_host_id;
+ request_id_.request_id = info->request_id;
+
+ if (!tab_util::GetTabContentsID(request,
+ &render_process_host_id_,
+ &tab_contents_id_))
+ NOTREACHED();
+
+ // This makes sure we don't disappear on the IO thread until we've given an
+ // answer to the URLRequest.
+ //
+ // Release in CompleteCancelRequest, CompleteContinueRequest,
+ // CompleteStartRequest or CompleteTakeNoAction.
+ AddRef();
+}
+
+void SSLManager::ErrorHandler::Dispatch() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ TabContents* web_contents =
+ tab_util::GetWebContentsByID(render_process_host_id_, tab_contents_id_);
+
+ if (!web_contents) {
+ // We arrived on the UI thread, but the tab we're looking for is no longer
+ // here.
+ OnDispatchFailed();
+ return;
+ }
+
+ // Hand ourselves off to the SSLManager.
+ manager_ = web_contents->controller()->ssl_manager();
+ OnDispatched();
+}
+
+WebContents* SSLManager::ErrorHandler::GetWebContents() {
+ return tab_util::GetWebContentsByID(render_process_host_id_,
+ tab_contents_id_);
+}
+
+void SSLManager::ErrorHandler::CancelRequest() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ // We need to complete this task on the IO thread.
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SSLManager::ErrorHandler::CompleteCancelRequest,
+ net::ERR_ABORTED));
+}
+
+void SSLManager::ErrorHandler::DenyRequest() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ // We need to complete this task on the IO thread.
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SSLManager::ErrorHandler::CompleteCancelRequest,
+ net::ERR_INSECURE_RESPONSE));
+}
+
+void SSLManager::ErrorHandler::ContinueRequest() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ // We need to complete this task on the IO thread.
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SSLManager::ErrorHandler::CompleteContinueRequest));
+}
+
+void SSLManager::ErrorHandler::StartRequest(FilterPolicy::Type filter_policy) {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ // We need to complete this task on the IO thread.
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SSLManager::ErrorHandler::CompleteStartRequest, filter_policy));
+}
+
+void SSLManager::ErrorHandler::TakeNoAction() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+
+ // We need to complete this task on the IO thread.
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SSLManager::ErrorHandler::CompleteTakeNoAction));
+}
+
+void SSLManager::ErrorHandler::CompleteCancelRequest(int error) {
+ DCHECK(MessageLoop::current() == io_loop_);
+
+ // It is important that we notify the URLRequest only once. If we try to
+ // notify the request twice, it may no longer exist and |this| might have
+ // already have been deleted.
+ DCHECK(!request_has_been_notified_);
+
+ if (!request_has_been_notified_) {
+ URLRequest* request = resource_dispatcher_host_->GetURLRequest(request_id_);
+ if (request) {
+ // The request can be NULL if it was cancelled by the renderer (as the
+ // result of the user navigating to a new page from the location bar).
+ DLOG(INFO) << "CompleteCancelRequest() url: " << request->url().spec();
+ request->CancelWithError(error);
+ }
+ request_has_been_notified_ = true;
+
+ // We're done with this object on the IO thread.
+ Release();
+ }
+}
+
+void SSLManager::ErrorHandler::CompleteContinueRequest() {
+ DCHECK(MessageLoop::current() == io_loop_);
+
+ // It is important that we notify the URLRequest only once. If we try to
+ // notify the request twice, it may no longer exist and |this| might have
+ // already have been deleted.
+ DCHECK(!request_has_been_notified_);
+
+ if (!request_has_been_notified_) {
+ URLRequest* request = resource_dispatcher_host_->GetURLRequest(request_id_);
+ if (request) {
+ // The request can be NULL if it was cancelled by the renderer (as the
+ // result of the user navigating to a new page from the location bar).
+ DLOG(INFO) << "CompleteContinueRequest() url: " << request->url().spec();
+ request->ContinueDespiteLastError();
+ }
+ request_has_been_notified_ = true;
+
+ // We're done with this object on the IO thread.
+ Release();
+ }
+}
+
+void SSLManager::ErrorHandler::CompleteStartRequest(
+ FilterPolicy::Type filter_policy) {
+ DCHECK(MessageLoop::current() == io_loop_);
+
+ // It is important that we notify the URLRequest only once. If we try to
+ // notify the request twice, it may no longer exist and |this| might have
+ // already have been deleted.
+ DCHECK(!request_has_been_notified_);
+
+ if (request_has_been_notified_)
+ return;
+
+ URLRequest* request = resource_dispatcher_host_->GetURLRequest(request_id_);
+ if (request) {
+ // The request can be NULL if it was cancelled by the renderer (as the
+ // result of the user navigating to a new page from the location bar).
+ DLOG(INFO) << "CompleteStartRequest() url: " << request->url().spec();
+ // The request should not have been started (SUCCESS is the initial state).
+ DCHECK(request->status().status() == URLRequestStatus::SUCCESS);
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+ info->filter_policy = filter_policy;
+ request->Start();
+ }
+ request_has_been_notified_ = true;
+
+ // We're done with this object on the IO thread.
+ Release();
+}
+
+void SSLManager::ErrorHandler::CompleteTakeNoAction() {
+ DCHECK(MessageLoop::current() == io_loop_);
+
+ // It is important that we notify the URLRequest only once. If we try to
+ // notify the request twice, it may no longer exist and |this| might have
+ // already have been deleted.
+ DCHECK(!request_has_been_notified_);
+
+ if (!request_has_been_notified_) {
+ request_has_been_notified_ = true;
+
+ // We're done with this object on the IO thread.
+ Release();
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// CertError
+
+SSLManager::CertError::CertError(
+ ResourceDispatcherHost* rdh,
+ URLRequest* request,
+ ResourceType::Type resource_type,
+ int cert_error,
+ net::X509Certificate* cert,
+ MessageLoop* ui_loop)
+ : ErrorHandler(rdh, request, ui_loop),
+ cert_error_(cert_error),
+ resource_type_(resource_type) {
+ DCHECK(request == resource_dispatcher_host_->GetURLRequest(request_id_));
+
+ // We cannot use the request->ssl_info(), it's not been initialized yet, so
+ // we have to set the fields manually.
+ ssl_info_.cert = cert;
+ ssl_info_.SetCertError(cert_error);
+}
+
+// static
+void SSLManager::OnSSLCertificateError(ResourceDispatcherHost* rdh,
+ URLRequest* request,
+ int cert_error,
+ net::X509Certificate* cert,
+ MessageLoop* ui_loop) {
+ DLOG(INFO) << "OnSSLCertificateError() cert_error: " << cert_error <<
+ " url: " << request->url().spec();
+
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+ DCHECK(info);
+
+ // A certificate error occurred. Construct a CertError object and hand it
+ // over to the UI thread for processing.
+ ui_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(new CertError(rdh, request, info->resource_type,
+ cert_error, cert, ui_loop),
+ &CertError::Dispatch));
+}
+
+// static
+void SSLManager::OnMixedContentRequest(ResourceDispatcherHost* rdh,
+ URLRequest* request,
+ MessageLoop* ui_loop) {
+ ui_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(new MixedContentHandler(rdh, request, ui_loop),
+ &MixedContentHandler::Dispatch));
+}
+
+void SSLManager::OnCertError(CertError* error) {
+ // Ask our delegate to deal with the error.
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ // We might not have a navigation entry in some cases (e.g. when a
+ // HTTPS page opens a popup with no URL and then populate it with
+ // document.write()). See bug http://crbug.com/3845.
+ if (!entry)
+ return;
+
+ delegate()->OnCertError(entry->url(), error);
+}
+
+void SSLManager::OnMixedContent(MixedContentHandler* mixed_content) {
+ // Ask our delegate to deal with the mixed content.
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ // We might not have a navigation entry in some cases (e.g. when a
+ // HTTPS page opens a popup with no URL and then populate it with
+ // document.write()). See bug http://crbug.com/3845.
+ if (!entry)
+ return;
+
+ delegate()->OnMixedContent(controller_, entry->url(), mixed_content);
+}
+
+void SSLManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // We should only be getting notifications from our controller.
+ DCHECK(source == Source<NavigationController>(controller_));
+
+ // Dispatch by type.
+ switch (type) {
+ case NOTIFY_NAV_ENTRY_COMMITTED:
+ DidCommitProvisionalLoad(details);
+ break;
+ case NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR:
+ DidFailProvisionalLoadWithError(
+ Details<ProvisionalLoadDetails>(details).ptr());
+ break;
+ case NOTIFY_RESOURCE_RESPONSE_STARTED:
+ DidStartResourceResponse(Details<ResourceRequestDetails>(details).ptr());
+ break;
+ case NOTIFY_RESOURCE_RECEIVED_REDIRECT:
+ DidReceiveResourceRedirect(
+ Details<ResourceRedirectDetails>(details).ptr());
+ break;
+ case NOTIFY_LOAD_FROM_MEMORY_CACHE:
+ DidLoadFromMemoryCache(
+ Details<LoadFromMemoryCacheDetails>(details).ptr());
+ break;
+ default:
+ NOTREACHED() << "The SSLManager received an unexpected notification.";
+ }
+}
+
+void SSLManager::InitializeEntryIfNeeded(NavigationEntry* entry) {
+ DCHECK(entry);
+
+ // If the security style of the entry is SECURITY_STYLE_UNKNOWN, then it is a
+ // fresh entry and should get the default style.
+ if (entry->ssl().security_style() == SECURITY_STYLE_UNKNOWN) {
+ entry->ssl().set_security_style(
+ delegate()->GetDefaultStyle(entry->url()));
+ }
+}
+
+void SSLManager::NavigationStateChanged() {
+ NavigationEntry* active_entry = controller_->GetActiveEntry();
+ if (!active_entry)
+ return; // Nothing showing yet.
+
+ // This might be a new entry we've never seen before.
+ InitializeEntryIfNeeded(active_entry);
+}
+
+void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) {
+ DCHECK(details);
+
+ // Simulate loading this resource through the usual path.
+ // Note that we specify SUB_RESOURCE as the resource type as WebCore only
+ // caches sub-resources.
+ delegate()->OnRequestStarted(this, details->url(),
+ ResourceType::SUB_RESOURCE,
+ details->ssl_cert_id(),
+ details->ssl_cert_status());
+}
+
+void SSLManager::DidCommitProvisionalLoad(
+ const NotificationDetails& in_details) {
+ NavigationController::LoadCommittedDetails* details =
+ Details<NavigationController::LoadCommittedDetails>(in_details).ptr();
+
+ // Ignore in-page navigations, they should not change the security style or
+ // the info-bars.
+ if (details->is_in_page)
+ return;
+
+ // Decode the security details.
+ int ssl_cert_id, ssl_cert_status, ssl_security_bits;
+ DeserializeSecurityInfo(details->serialized_security_info,
+ &ssl_cert_id, &ssl_cert_status, &ssl_security_bits);
+
+ bool changed = false;
+ if (details->is_main_frame) {
+ // Update the SSL states of the pending entry.
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ if (entry) {
+ // We may not have an entry if this is a navigation to an initial blank
+ // page. Reset the SSL information and add the new data we have.
+ entry->ssl() = NavigationEntry::SSLStatus();
+ InitializeEntryIfNeeded(entry); // For security_style.
+ entry->ssl().set_cert_id(ssl_cert_id);
+ entry->ssl().set_cert_status(ssl_cert_status);
+ entry->ssl().set_security_bits(ssl_security_bits);
+ changed = true;
+ }
+
+ ShowPendingMessages();
+ }
+
+ // An HTTPS response may not have a certificate for some reason. When that
+ // happens, use the unauthenticated (HTTP) rather than the authentication
+ // broken security style so that we can detect this error condition.
+ if (net::IsCertStatusError(ssl_cert_status)) {
+ changed |= SetMaxSecurityStyle(SECURITY_STYLE_AUTHENTICATION_BROKEN);
+ if (!details->is_main_frame &&
+ !details->entry->ssl().has_unsafe_content()) {
+ details->entry->ssl().set_has_unsafe_content();
+ changed = true;
+ }
+ } else if (details->entry->url().SchemeIsSecure() && !ssl_cert_id) {
+ if (details->is_main_frame) {
+ changed |= SetMaxSecurityStyle(SECURITY_STYLE_UNAUTHENTICATED);
+ } else {
+ // If the frame has been blocked we keep our security style as
+ // authenticated in that case as nothing insecure is actually showing or
+ // loaded.
+ if (!details->is_content_filtered &&
+ !details->entry->ssl().has_mixed_content()) {
+ details->entry->ssl().set_has_mixed_content();
+ changed = true;
+ }
+ }
+ }
+
+ if (changed) {
+ // Only send the notification when something actually changed.
+ NotificationService::current()->Notify(
+ NOTIFY_SSL_STATE_CHANGED,
+ Source<NavigationController>(controller_),
+ NotificationService::NoDetails());
+ }
+}
+
+void SSLManager::DidFailProvisionalLoadWithError(
+ ProvisionalLoadDetails* details) {
+ DCHECK(details);
+
+ // Ignore in-page navigations.
+ if (details->in_page_navigation())
+ return;
+
+ if (details->main_frame())
+ ClearPendingMessages();
+}
+
+void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) {
+ DCHECK(details);
+
+ // Notify our delegate that we started a resource request. Ideally, the
+ // delegate should have the ability to cancel the request, but we can't do
+ // that yet.
+ delegate()->OnRequestStarted(this, details->url(),
+ details->resource_type(),
+ details->ssl_cert_id() ,
+ details->ssl_cert_status());
+}
+
+void SSLManager::DidReceiveResourceRedirect(ResourceRedirectDetails* details) {
+ // TODO(jcampan): when we receive a redirect for a sub-resource, we may want
+ // to clear any mixed/unsafe content error that it may have triggered.
+}
+
+void SSLManager::ShowPendingMessages() {
+ std::vector<SSLMessageInfo>::const_iterator iter;
+ for (iter = pending_messages_.begin();
+ iter != pending_messages_.end(); ++iter) {
+ ShowMessageWithLink(iter->message, iter->link_text, iter->action);
+ }
+ ClearPendingMessages();
+}
+
+void SSLManager::ClearPendingMessages() {
+ pending_messages_.clear();
+}
+
+// static
+std::string SSLManager::SerializeSecurityInfo(int cert_id,
+ int cert_status,
+ int security_bits) {
+ Pickle pickle;
+ pickle.WriteInt(cert_id);
+ pickle.WriteInt(cert_status);
+ pickle.WriteInt(security_bits);
+ return std::string(static_cast<const char*>(pickle.data()), pickle.size());
+}
+
+// static
+bool SSLManager::DeserializeSecurityInfo(const std::string& state,
+ int* cert_id,
+ int* cert_status,
+ int* security_bits) {
+ DCHECK(cert_id && cert_status && security_bits);
+ if (state.empty()) {
+ // No SSL used.
+ *cert_id = 0;
+ *cert_status = 0;
+ *security_bits = -1;
+ return false;
+ }
+
+ Pickle pickle(state.data(), static_cast<int>(state.size()));
+ void * iter = NULL;
+ pickle.ReadInt(&iter, cert_id);
+ pickle.ReadInt(&iter, cert_status);
+ pickle.ReadInt(&iter, security_bits);
+ return true;
+}
+
+// static
+bool SSLManager::GetEVCertNames(const net::X509Certificate& cert,
+ std::wstring* short_name,
+ std::wstring* ca_name) {
+ DCHECK(short_name || ca_name);
+
+ // EV are required to have an organization name and country.
+ if (cert.subject().organization_names.empty() ||
+ cert.subject().country_name.empty()) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (short_name) {
+ *short_name = l10n_util::GetStringF(
+ IDS_SECURE_CONNECTION_EV,
+ UTF8ToWide(cert.subject().organization_names[0]),
+ UTF8ToWide(cert.subject().country_name));
+ }
+
+ if (ca_name) {
+ // TODO(wtc): should we show the root CA's name instead?
+ *ca_name = l10n_util::GetStringF(
+ IDS_SECURE_CONNECTION_EV_CA,
+ UTF8ToWide(cert.issuer().organization_names[0]));
+ }
+ return true;
+}
+
diff --git a/chrome/browser/ssl/ssl_manager.h b/chrome/browser/ssl/ssl_manager.h
new file mode 100644
index 0000000..e650b35
--- /dev/null
+++ b/chrome/browser/ssl/ssl_manager.h
@@ -0,0 +1,465 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_SSL_MANAGER_H_
+#define CHROME_BROWSER_SSL_MANAGER_H_
+
+#include <string>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/security_style.h"
+#include "chrome/browser/tab_contents/provisional_load_details.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/base/ssl_info.h"
+#include "net/base/x509_certificate.h"
+#include "webkit/glue/console_message_level.h"
+#include "webkit/glue/resource_type.h"
+
+class AutomationProvider;
+class NavigationEntry;
+class LoadFromMemoryCacheDetails;
+class LoadNotificationDetails;
+class NavigationController;
+class PrefService;
+class ResourceRedirectDetails;
+class ResourceRequestDetails;
+class SSLErrorInfo;
+class Task;
+class URLRequest;
+class WebContents;
+
+// The SSLManager SSLManager controls the SSL UI elements in a TabContents. It
+// listens for various events that influence when these elements should or
+// should not be displayed and adjusts them accordingly.
+//
+// There is one SSLManager per tab.
+// The security state (secure/insecure) is stored in the navigation entry.
+// Along with it are stored any SSL error code and the associated cert.
+//
+
+class SSLManager : public NotificationObserver {
+ public:
+ // An ErrorHandler carries information from the IO thread to the UI thread
+ // and is dispatched to the appropriate SSLManager when it arrives on the
+ // UI thread. Subclasses should override the OnDispatched/OnDispatchFailed
+ // methods to implement the actions that should be taken on the UI thread.
+ // These methods can call the different convenience methods ContinueRequest/
+ // CancelRequest/StartRequest to perform any required action on the URLRequest
+ // the ErrorHandler was created with.
+ // IMPORTANT NOTE: if you are not doing anything in
+ // OnDispatched/OnDispatchFailed, make sure you call TakeNoAction(). This is
+ // necessary for ensuring the instance is not leaked.
+ class ErrorHandler : public base::RefCountedThreadSafe<ErrorHandler> {
+ public:
+ virtual ~ErrorHandler() { }
+
+ // Find the appropriate SSLManager for the URLRequest and begin handling
+ // this error.
+ //
+ // Call on UI thread.
+ void Dispatch();
+
+ // Available on either thread.
+ const GURL& request_url() const { return request_url_; }
+
+ // Call on the UI thread.
+ SSLManager* manager() const { return manager_; };
+
+ // Returns the WebContents this object is associated with. Should be
+ // called from the UI thread.
+ WebContents* GetWebContents();
+
+ // Cancels the associated URLRequest.
+ // This method can be called from OnDispatchFailed and OnDispatched.
+ void CancelRequest();
+
+ // Continue the URLRequest ignoring any previous errors. Note that some
+ // errors cannot be ignored, in which case this will result in the request
+ // being canceled.
+ // This method can be called from OnDispatchFailed and OnDispatched.
+ void ContinueRequest();
+
+ // Cancels the associated URLRequest and mark it as denied. The renderer
+ // processes such request in a special manner, optionally replacing them
+ // with alternate content (typically frames content is replaced with a
+ // warning message).
+ // This method can be called from OnDispatchFailed and OnDispatched.
+ void DenyRequest();
+
+ // Starts the associated URLRequest. |filter_policy| specifies whether the
+ // ResourceDispatcher should attempt to filter the loaded content in order
+ // to make it secure (ex: images are made slightly transparent and are
+ // stamped).
+ // Should only be called when the URLRequest has not already been started.
+ // This method can be called from OnDispatchFailed and OnDispatched.
+ void StartRequest(FilterPolicy::Type filter_policy);
+
+ // Does nothing on the URLRequest but ensures the current instance ref
+ // count is decremented appropriately. Subclasses that do not want to
+ // take any specific actions in their OnDispatched/OnDispatchFailed should
+ // call this.
+ void TakeNoAction();
+
+ protected:
+ // Construct on the IO thread.
+ ErrorHandler(ResourceDispatcherHost* resource_dispatcher_host,
+ URLRequest* request,
+ MessageLoop* ui_loop);
+
+ // The following 2 methods are the methods subclasses should implement.
+ virtual void OnDispatchFailed() { TakeNoAction(); }
+
+ // Can use the manager_ member.
+ virtual void OnDispatched() { TakeNoAction(); }
+
+ // We cache the message loops to be able to proxy events across the thread
+ // boundaries.
+ MessageLoop* ui_loop_;
+ MessageLoop* io_loop_;
+
+ // Should only be accessed on the UI thread.
+ SSLManager* manager_; // Our manager.
+
+ // The id of the URLRequest associated with this object.
+ // Should only be accessed from the IO thread.
+ ResourceDispatcherHost::GlobalRequestID request_id_;
+
+ // The ResourceDispatcherHost we are associated with.
+ ResourceDispatcherHost* resource_dispatcher_host_;
+
+ private:
+ // Completes the CancelRequest operation on the IO thread.
+ // Call on the IO thread.
+ void CompleteCancelRequest(int error);
+
+ // Completes the ContinueRequest operation on the IO thread.
+ //
+ // Call on the IO thread.
+ void CompleteContinueRequest();
+
+ // Completes the StartRequest operation on the IO thread.
+ // Call on the IO thread.
+ void CompleteStartRequest(FilterPolicy::Type filter_policy);
+
+ // Derefs this instance.
+ // Call on the IO thread.
+ void CompleteTakeNoAction();
+
+ // We use these members to find the correct SSLManager when we arrive on
+ // the UI thread.
+ int render_process_host_id_;
+ int tab_contents_id_;
+
+ // This read-only member can be accessed on any thread.
+ const GURL request_url_; // The URL that we requested.
+
+ // Should only be accessed on the IO thread
+ bool request_has_been_notified_; // A flag to make sure we notify the
+ // URLRequest exactly once.
+
+ DISALLOW_EVIL_CONSTRUCTORS(ErrorHandler);
+ };
+
+ // A CertError represents an error that occurred with the certificate in an
+ // SSL session. A CertError object exists both on the IO thread and on the UI
+ // thread and allows us to cancel/continue a request it is associated with.
+ class CertError : public ErrorHandler {
+ public:
+ // These accessors are available on either thread
+ const net::SSLInfo& ssl_info() const { return ssl_info_; }
+ int cert_error() const { return cert_error_; }
+
+ ResourceType::Type resource_type() const { return resource_type_; }
+ private:
+ // SSLManager is responsible for creating CertError objects.
+ friend class SSLManager;
+
+ // Construct on the IO thread.
+ // We mark this method as private because it is tricky to correctly
+ // construct a CertError object.
+ CertError(ResourceDispatcherHost* resource_dispatcher_host,
+ URLRequest* request,
+ ResourceType::Type resource_type,
+ int cert_error,
+ net::X509Certificate* cert,
+ MessageLoop* ui_loop);
+
+ // ErrorHandler methods
+ virtual void OnDispatchFailed() { CancelRequest(); }
+ virtual void OnDispatched() { manager_->OnCertError(this); }
+
+ // These read-only members can be accessed on any thread.
+ net::SSLInfo ssl_info_;
+ const int cert_error_; // The error we represent.
+
+ // What kind of resource is associated with the requested that generated
+ // that error.
+ ResourceType::Type resource_type_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CertError);
+ };
+
+ // The MixedContentHandler class is used to query what to do with
+ // mixed content, from the IO thread to the UI thread.
+ class MixedContentHandler : public ErrorHandler {
+ public:
+ // Created on the IO thread.
+ MixedContentHandler(ResourceDispatcherHost* rdh,
+ URLRequest* request,
+ MessageLoop* ui_loop)
+ : ErrorHandler(rdh, request, ui_loop) { }
+
+ protected:
+ virtual void OnDispatchFailed() { TakeNoAction(); }
+ virtual void OnDispatched() { manager()->OnMixedContent(this); }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(MixedContentHandler);
+ };
+
+ // The SSLManager will ask its delegate to decide how to handle events
+ // relevant to SSL. Delegates are expected to be stateless and intended to be
+ // easily implementable.
+ //
+ // Delegates should interact with the rest of the browser only through their
+ // parameters and through the delegate API of the SSLManager.
+ //
+ // If a delegate needs to do something tricky, consider having the SSLManager
+ // do it instead.
+ class Delegate {
+ public:
+ // An error occurred with the certificate in an SSL connection.
+ virtual void OnCertError(const GURL& main_frame_url, CertError* error) = 0;
+
+ // A request for a mixed-content resource was made. Note that the resource
+ // request was not started yet and the delegate is responsible for starting
+ // it.
+ virtual void OnMixedContent(
+ NavigationController* navigation_controller,
+ const GURL& main_frame_url,
+ MixedContentHandler* mixed_content_handler) = 0;
+
+ // We have started a resource request for the given URL.
+ virtual void OnRequestStarted(SSLManager* manager,
+ const GURL& url,
+ ResourceType::Type resource_type,
+ int ssl_cert_id,
+ int ssl_cert_status) = 0;
+
+ // Returns the default security style for a given URL.
+ virtual SecurityStyle GetDefaultStyle(const GURL& url) = 0;
+ };
+
+ static void RegisterUserPrefs(PrefService* prefs);
+
+ // Construct an SSLManager for the specified tab.
+ // If |delegate| is NULL, SSLPolicy::GetDefaultPolicy() is used.
+ SSLManager(NavigationController* controller, Delegate* delegate);
+
+ ~SSLManager();
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Delegate API
+ //
+ // The SSL manager expects these methods to be called by its delegate. They
+ // exist to make Delegates easy to implement.
+
+ // Ensure that the specified message is displayed to the user. This will
+ // display an InfoBar at the top of the associated tab.
+ void ShowMessage(const std::wstring& msg);
+
+ // Same as ShowMessage but also contains a link that when clicked run the
+ // specified task. The SSL Manager becomes the owner of the task.
+ void ShowMessageWithLink(const std::wstring& msg,
+ const std::wstring& link_text,
+ Task* task);
+
+ // Sets the maximum security style for the page. If the current security
+ // style is lower than |style|, this will not have an effect on the security
+ // indicators.
+ //
+ // It will return true if the navigation entry was updated or false if
+ // nothing changed. The caller is responsible for broadcasting
+ // NOTIFY_SSY_STATE_CHANGED if it returns true.
+ bool SetMaxSecurityStyle(SecurityStyle style);
+
+ // Logs a message to the console of the page.
+ void AddMessageToConsole(const std::wstring& msg,
+ ConsoleMessageLevel level);
+
+ // Records that |cert| is permitted to be used for |host| in the future.
+ void DenyCertForHost(net::X509Certificate* cert, const std::string& host);
+
+ // Records that |cert| is not permitted to be used for |host| in the future.
+ void AllowCertForHost(net::X509Certificate* cert, const std::string& host);
+
+ // Queries whether |cert| is allowed or denied for |host|.
+ net::X509Certificate::Policy::Judgment QueryPolicy(
+ net::X509Certificate* cert, const std::string& host);
+
+ // Allow mixed/unsafe content to be visible (non filtered) for the specified
+ // URL.
+ // Note that the current implementation allows on a host name basis.
+ void AllowShowInsecureContentForURL(const GURL& url);
+
+ // Returns whether the specified URL is allowed to show insecure (mixed or
+ // unsafe) content.
+ bool CanShowInsecureContent(const GURL& url);
+
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // The delegate of the SSLManager. This value may be changed at any time,
+ // but it is not permissible for it to be NULL.
+ Delegate* delegate() const { return delegate_; }
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+ // Entry point for SSLCertificateErrors. This function begins the process
+ // of resolving a certificate error during an SSL connection. SSLManager
+ // will adjust the security UI and either call |Cancel| or
+ // |ContinueDespiteLastError| on the URLRequest.
+ //
+ // Called on the IO thread.
+ static void OnSSLCertificateError(ResourceDispatcherHost* resource_dispatcher,
+ URLRequest* request,
+ int cert_error,
+ net::X509Certificate* cert,
+ MessageLoop* ui_loop);
+
+ // Called when a mixed-content sub-resource request has been detected. The
+ // request is not started yet. The SSLManager will make a decision on whether
+ // to filter that request's content (with the filter_policy flag).
+ // TODO (jcampan): Implement a way to just cancel the request. This is not
+ // straight-forward as canceling a request that has not been started will
+ // not remove from the pending_requests_ of the ResourceDispatcherHost.
+ // Called on the IO thread.
+ static void OnMixedContentRequest(ResourceDispatcherHost* resource_dispatcher,
+ URLRequest* request,
+ MessageLoop* ui_loop);
+
+ // Called by CertError::Dispatch to kick off processing of the cert error by
+ // the SSL manager. The error originated from the ResourceDispatcherHost.
+ //
+ // Called on the UI thread.
+ void OnCertError(CertError* error);
+
+ // Called by MixedContentHandler::Dispatch to kick off processing of the
+ // mixed-content resource request. The info originated from the
+ // ResourceDispatcherHost.
+ //
+ // Called on the UI thread.
+ void OnMixedContent(MixedContentHandler* mixed_content);
+
+ // Entry point for navigation. This function begins the process of updating
+ // the security UI when the main frame navigates to a new URL.
+ //
+ // Called on the UI thread.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Entry point for navigation. This function begins the process of updating
+ // the security UI when the main frame navigates.
+ //
+ // Called on the UI thread.
+ void NavigationStateChanged();
+
+ // Called to determine if there were any processed SSL errors from request.
+ bool ProcessedSSLErrorFromRequest() const;
+
+ NavigationController* controller() { return controller_; }
+
+ // Convenience methods for serializing/deserializing the security info.
+ static std::string SerializeSecurityInfo(int cert_id,
+ int cert_status,
+ int security_bits);
+ static bool DeserializeSecurityInfo(const std::string& state,
+ int* cert_id,
+ int* cert_status,
+ int* security_bits);
+
+ // Sets |short_name| to <organization_name> [<country>] and |ca_name|
+ // to something like:
+ // "Verified by <issuer_organization_name>"
+ static bool GetEVCertNames(const net::X509Certificate& cert,
+ std::wstring* short_name,
+ std::wstring* ca_name);
+
+ private:
+ // SSLMessageInfo contains the information necessary for displaying a message
+ // in an info-bar.
+ struct SSLMessageInfo {
+ public:
+ explicit SSLMessageInfo(const std::wstring& text)
+ : message(text),
+ action(NULL) { }
+ SSLMessageInfo(const std::wstring& message,
+ const std::wstring& link_text,
+ Task* action)
+ : message(message), link_text(link_text), action(action) { }
+
+ // Overridden so that std::find works.
+ bool operator==(const std::wstring& other_message) const {
+ // We are uniquing SSLMessageInfo by their message only.
+ return message == other_message;
+ }
+
+ std::wstring message;
+ std::wstring link_text;
+ Task* action;
+ };
+
+ // Entry points for notifications to which we subscribe. Note that
+ // DidCommitProvisionalLoad uses the abstract NotificationDetails type since
+ // the type we need is in NavigationController which would create a circular
+ // header file dependency.
+ void DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details);
+ void DidCommitProvisionalLoad(const NotificationDetails& details);
+ void DidFailProvisionalLoadWithError(ProvisionalLoadDetails* details);
+ void DidStartResourceResponse(ResourceRequestDetails* details);
+ void DidReceiveResourceRedirect(ResourceRedirectDetails* details);
+
+ // Convenience method for initializing navigation entries.
+ void InitializeEntryIfNeeded(NavigationEntry* entry);
+
+ // Shows the pending messages (in info-bars) if any.
+ void ShowPendingMessages();
+
+ // Clears any pending messages.
+ void ClearPendingMessages();
+
+ // Our delegate. The delegate is responsible for making policy decisions.
+ // Must not be NULL.
+ Delegate* delegate_;
+
+ // The NavigationController that owns this SSLManager. We are responsible
+ // for the security UI of this tab.
+ NavigationController* controller_;
+
+ // Handles registering notifications with the NotificationService.
+ NotificationRegistrar registrar_;
+
+ // Certificate policies for each host.
+ std::map<std::string, net::X509Certificate::Policy> cert_policy_for_host_;
+
+ // Domains for which it is OK to show insecure content.
+ std::set<std::string> can_show_insecure_content_for_host_;
+
+ // The list of messages that should be displayed (in info bars) when the page
+ // currently loading had loaded.
+ std::vector<SSLMessageInfo> pending_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLManager);
+};
+
+#endif // CHROME_BROWSER_SSL_MANAGER_H_
+
diff --git a/chrome/browser/ssl/ssl_policy.cc b/chrome/browser/ssl/ssl_policy.cc
new file mode 100644
index 0000000..e857244
--- /dev/null
+++ b/chrome/browser/ssl/ssl_policy.cc
@@ -0,0 +1,483 @@
+// Copyright (c) 2006-2008 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/browser/ssl/ssl_policy.h"
+
+#include "base/singleton.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_resources.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/ssl/ssl_error_info.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/web_contents.h"
+#include "chrome/common/jstemplate_builder.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/time_format.h"
+#include "generated_resources.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/ssl_info.h"
+#include "webkit/glue/console_message_level.h"
+#include "webkit/glue/resource_type.h"
+
+// Wrap all these helper classes in an anonymous namespace.
+namespace {
+
+static const char kDot = '.';
+
+class ShowUnsafeContentTask : public Task {
+ public:
+ ShowUnsafeContentTask(const GURL& main_frame_url,
+ SSLManager::ErrorHandler* error_handler);
+ virtual ~ShowUnsafeContentTask();
+
+ virtual void Run();
+
+ private:
+ scoped_refptr<SSLManager::ErrorHandler> error_handler_;
+ GURL main_frame_url_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ShowUnsafeContentTask);
+};
+
+ShowUnsafeContentTask::ShowUnsafeContentTask(
+ const GURL& main_frame_url,
+ SSLManager::ErrorHandler* error_handler)
+ : main_frame_url_(main_frame_url),
+ error_handler_(error_handler) {
+}
+
+ShowUnsafeContentTask::~ShowUnsafeContentTask() {
+}
+
+void ShowUnsafeContentTask::Run() {
+ error_handler_->manager()->AllowShowInsecureContentForURL(main_frame_url_);
+ // Reload the page.
+ error_handler_->GetWebContents()->controller()->Reload(true);
+}
+
+static void ShowErrorPage(SSLPolicy* policy, SSLManager::CertError* error) {
+ SSLErrorInfo error_info = policy->GetSSLErrorInfo(error);
+
+ // Let's build the html error page.
+ DictionaryValue strings;
+ strings.SetString(L"title", l10n_util::GetString(IDS_SSL_ERROR_PAGE_TITLE));
+ strings.SetString(L"headLine", error_info.title());
+ strings.SetString(L"description", error_info.details());
+ strings.SetString(L"moreInfoTitle",
+ l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_TITLE));
+ SSLBlockingPage::SetExtraInfo(&strings, error_info.extra_information());
+
+ strings.SetString(L"back", l10n_util::GetString(IDS_SSL_ERROR_PAGE_BACK));
+
+ strings.SetString(L"textdirection",
+ (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ?
+ L"rtl" : L"ltr");
+
+ static const StringPiece html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_SSL_ERROR_HTML));
+
+ std::string html_text(jstemplate_builder::GetTemplateHtml(html, &strings,
+ "template_root"));
+
+ WebContents* tab = error->GetWebContents();
+ int cert_id = CertStore::GetSharedInstance()->StoreCert(
+ error->ssl_info().cert, tab->render_view_host()->process()->host_id());
+ std::string security_info =
+ SSLManager::SerializeSecurityInfo(cert_id,
+ error->ssl_info().cert_status,
+ error->ssl_info().security_bits);
+ tab->render_view_host()->LoadAlternateHTMLString(html_text,
+ true,
+ error->request_url(),
+ security_info);
+ tab->controller()->GetActiveEntry()->set_page_type(
+ NavigationEntry::ERROR_PAGE);
+}
+
+static void ShowBlockingPage(SSLPolicy* policy, SSLManager::CertError* error) {
+ SSLBlockingPage* blocking_page = new SSLBlockingPage(error, policy);
+ blocking_page->Show();
+}
+
+static bool IsIntranetHost(const std::string& host) {
+ const size_t dot = host.find(kDot);
+ return dot == std::basic_string<CHAR>::npos ||
+ dot == host.length() - 1;
+}
+
+class CommonNameInvalidPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<CommonNameInvalidPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ OnOverridableCertError(main_frame_url, error);
+ }
+};
+
+class DateInvalidPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<DateInvalidPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ OnOverridableCertError(main_frame_url, error);
+ }
+};
+
+class AuthorityInvalidPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<AuthorityInvalidPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ OnOverridableCertError(main_frame_url, error);
+ }
+};
+
+class ContainsErrorsPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<ContainsErrorsPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ OnFatalCertError(main_frame_url, error);
+ }
+};
+
+class NoRevocationMechanismPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<NoRevocationMechanismPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ // Silently ignore this error.
+ error->ContinueRequest();
+ }
+};
+
+class UnableToCheckRevocationPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<UnableToCheckRevocationPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ // We ignore this error and display an info-bar.
+ error->ContinueRequest();
+ error->manager()->ShowMessage(l10n_util::GetString(
+ IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_INFO_BAR));
+ }
+};
+
+class RevokedPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<RevokedPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ OnFatalCertError(main_frame_url, error);
+ }
+};
+
+class InvalidPolicy : public SSLPolicy {
+ public:
+ static SSLPolicy* GetInstance() {
+ return Singleton<InvalidPolicy>::get();
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ OnFatalCertError(main_frame_url, error);
+ }
+};
+
+class DefaultPolicy : public SSLPolicy {
+ public:
+ DefaultPolicy() {
+ // Load our helper classes to handle various cert errors.
+ DCHECK(SubPolicyIndex(net::ERR_CERT_COMMON_NAME_INVALID) == 0);
+ sub_policies_[0] = CommonNameInvalidPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_DATE_INVALID) == 1);
+ sub_policies_[1] = DateInvalidPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_AUTHORITY_INVALID) == 2);
+ sub_policies_[2] = AuthorityInvalidPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_CONTAINS_ERRORS) == 3);
+ sub_policies_[3] = ContainsErrorsPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_NO_REVOCATION_MECHANISM) == 4);
+ sub_policies_[4] = NoRevocationMechanismPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION) == 5);
+ sub_policies_[5] = UnableToCheckRevocationPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_REVOKED) == 6);
+ sub_policies_[6] = RevokedPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_INVALID) == 7);
+ sub_policies_[7] = InvalidPolicy::GetInstance();
+ DCHECK(SubPolicyIndex(net::ERR_CERT_END) == 8);
+ }
+
+ void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ int index = SubPolicyIndex(error->cert_error());
+ if (index < 0 || index >= arraysize(sub_policies_)) {
+ NOTREACHED();
+ error->CancelRequest();
+ return;
+ }
+
+ // First we check if we know the policy for this error.
+ net::X509Certificate::Policy::Judgment judgment =
+ error->manager()->QueryPolicy(error->ssl_info().cert,
+ error->request_url().host());
+
+ switch (judgment) {
+ case net::X509Certificate::Policy::ALLOWED:
+ // We've been told to allow this certificate.
+ if (error->manager()->SetMaxSecurityStyle(
+ SECURITY_STYLE_AUTHENTICATION_BROKEN)) {
+ NotificationService::current()->Notify(
+ NOTIFY_SSL_STATE_CHANGED,
+ Source<NavigationController>(error->manager()->controller()),
+ Details<NavigationEntry>(
+ error->manager()->controller()->GetActiveEntry()));
+ }
+ error->ContinueRequest();
+ break;
+ case net::X509Certificate::Policy::DENIED:
+ // For now we handle the DENIED as the UNKNOWN, which means a blocking
+ // page is shown to the user every time he comes back to the page.
+ case net::X509Certificate::Policy::UNKNOWN:
+ // We don't know how to handle this error. Ask our sub-policies.
+ sub_policies_[index]->OnCertError(main_frame_url, error);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ void OnMixedContent(NavigationController* navigation_controller,
+ const GURL& main_frame_url,
+ SSLManager::MixedContentHandler* mixed_content_handler) {
+ PrefService* prefs = navigation_controller->profile()->GetPrefs();
+ FilterPolicy::Type filter_policy = FilterPolicy::DONT_FILTER;
+ if (!mixed_content_handler->manager()->
+ CanShowInsecureContent(main_frame_url)) {
+ filter_policy = FilterPolicy::FromInt(
+ prefs->GetInteger(prefs::kMixedContentFiltering));
+ }
+ if (filter_policy != FilterPolicy::DONT_FILTER) {
+ mixed_content_handler->manager()->ShowMessageWithLink(
+ l10n_util::GetString(IDS_SSL_INFO_BAR_FILTERED_CONTENT),
+ l10n_util::GetString(IDS_SSL_INFO_BAR_SHOW_CONTENT),
+ new ShowUnsafeContentTask(main_frame_url, mixed_content_handler));
+ }
+ mixed_content_handler->StartRequest(filter_policy);
+
+ NavigationEntry* entry = navigation_controller->GetLastCommittedEntry();
+ DCHECK(entry);
+ // Even though we are loading the mixed-content resource, it will not be
+ // included in the page when we set the policy to FILTER_ALL or
+ // FILTER_ALL_EXCEPT_IMAGES (only images and they are stamped with warning
+ // icons), so we don't set the mixed-content mode in these cases.
+ if (filter_policy == FilterPolicy::DONT_FILTER)
+ entry->ssl().set_has_mixed_content();
+
+ // Print a message indicating the mixed-contents resource in the console.
+ const std::wstring& msg = l10n_util::GetStringF(
+ IDS_MIXED_CONTENT_LOG_MESSAGE,
+ UTF8ToWide(entry->url().spec()),
+ UTF8ToWide(mixed_content_handler->request_url().spec()));
+ mixed_content_handler->manager()->
+ AddMessageToConsole(msg, MESSAGE_LEVEL_WARNING);
+
+ NotificationService::current()->Notify(
+ NOTIFY_SSL_STATE_CHANGED,
+ Source<NavigationController>(navigation_controller),
+ Details<NavigationEntry>(entry));
+ }
+
+ void OnDenyCertificate(SSLManager::CertError* error) {
+ int index = SubPolicyIndex(error->cert_error());
+ if (index < 0 || index >= arraysize(sub_policies_)) {
+ NOTREACHED();
+ return;
+ }
+ sub_policies_[index]->OnDenyCertificate(error);
+ }
+
+ void OnAllowCertificate(SSLManager::CertError* error) {
+ int index = SubPolicyIndex(error->cert_error());
+ if (index < 0 || index >= arraysize(sub_policies_)) {
+ NOTREACHED();
+ return;
+ }
+ sub_policies_[index]->OnAllowCertificate(error);
+ }
+
+ private:
+ // Returns the index of the sub-policy for |cert_error| in the
+ // sub_policies_ array.
+ int SubPolicyIndex(int cert_error) {
+ // Certificate errors are negative integers from net::ERR_CERT_BEGIN
+ // (inclusive) to net::ERR_CERT_END (exclusive) in *decreasing* order.
+ return net::ERR_CERT_BEGIN - cert_error;
+ }
+ SSLPolicy* sub_policies_[net::ERR_CERT_BEGIN - net::ERR_CERT_END];
+};
+
+} // namespace
+
+SSLPolicy* SSLPolicy::GetDefaultPolicy() {
+ // Lazily initialize our default policy instance.
+ static SSLPolicy* default_policy = new DefaultPolicy();
+ return default_policy;
+}
+
+SSLPolicy::SSLPolicy() {
+}
+
+void SSLPolicy::OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ // Default to secure behavior.
+ error->CancelRequest();
+}
+
+void SSLPolicy::OnRequestStarted(SSLManager* manager, const GURL& url,
+ ResourceType::Type resource_type,
+ int ssl_cert_id, int ssl_cert_status) {
+ // These schemes never leave the browser and don't require a warning.
+ if (url.SchemeIs("data") || url.SchemeIs("javascript") ||
+ url.SchemeIs("about"))
+ return;
+
+ NavigationEntry* entry = manager->controller()->GetActiveEntry();
+ if (!entry) {
+ // We may not have an entry for cases such as the inspector.
+ return;
+ }
+
+ NavigationEntry::SSLStatus& ssl = entry->ssl();
+ bool changed = false;
+ if (!entry->url().SchemeIsSecure() || // Current page is not secure.
+ resource_type == ResourceType::MAIN_FRAME || // Main frame load.
+ net::IsCertStatusError(ssl.cert_status())) { // There is already
+ // an error for the main page, don't report sub-resources as unsafe
+ // content.
+ // No mixed/unsafe content check necessary.
+ return;
+ }
+
+ if (url.SchemeIsSecure()) {
+ // Check for insecure content (anything served over intranet is considered
+ // insecure).
+
+ // TODO(jcampan): bug #1178228 Disabling the broken style for intranet
+ // hosts for beta as it is missing error strings (and cert status).
+ // if (IsIntranetHost(url.host()) ||
+ // net::IsCertStatusError(ssl_cert_status)) {
+ if (net::IsCertStatusError(ssl_cert_status)) {
+ // The resource is unsafe.
+ if (!ssl.has_unsafe_content()) {
+ changed = true;
+ ssl.set_has_unsafe_content();
+ manager->SetMaxSecurityStyle(SECURITY_STYLE_AUTHENTICATION_BROKEN);
+ }
+ }
+ }
+
+ if (changed) {
+ // Only send the notification when something actually changed.
+ NotificationService::current()->Notify(
+ NOTIFY_SSL_STATE_CHANGED,
+ Source<NavigationController>(manager->controller()),
+ NotificationService::NoDetails());
+ }
+}
+
+SecurityStyle SSLPolicy::GetDefaultStyle(const GURL& url) {
+ // Show the secure style for HTTPS.
+ if (url.SchemeIsSecure()) {
+ // TODO(jcampan): bug #1178228 Disabling the broken style for intranet
+ // hosts for beta as it is missing error strings (and cert status).
+ // CAs issue certs for intranet hosts to anyone.
+ // if (IsIntranetHost(url.host()))
+ // return SECURITY_STYLE_AUTHENTICATION_BROKEN;
+
+ return SECURITY_STYLE_AUTHENTICATED;
+ }
+
+ // Otherwise, show the unauthenticated style.
+ return SECURITY_STYLE_UNAUTHENTICATED;
+}
+
+SSLErrorInfo SSLPolicy::GetSSLErrorInfo(SSLManager::CertError* error) {
+ return SSLErrorInfo::CreateError(
+ SSLErrorInfo::NetErrorToErrorType(error->cert_error()),
+ error->ssl_info().cert, error->request_url());
+}
+
+void SSLPolicy::OnDenyCertificate(SSLManager::CertError* error) {
+ // Default behavior for rejecting a certificate.
+ error->CancelRequest();
+ error->manager()->DenyCertForHost(error->ssl_info().cert,
+ error->request_url().host());
+}
+
+void SSLPolicy::OnAllowCertificate(SSLManager::CertError* error) {
+ // Default behavior for accepting a certificate.
+ // Note that we should not call SetMaxSecurityStyle here, because the active
+ // NavigationEntry has just been deleted (in HideInterstitialPage) and the
+ // new NavigationEntry will not be set until DidNavigate. This is ok,
+ // because the new NavigationEntry will have its max security style set
+ // within DidNavigate.
+ error->ContinueRequest();
+ error->manager()->AllowCertForHost(error->ssl_info().cert,
+ error->request_url().host());
+}
+
+void SSLPolicy::OnOverridableCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ if (error->resource_type() != ResourceType::MAIN_FRAME) {
+ // A sub-resource has a certificate error. The user doesn't really
+ // have a context for making the right decision, so block the
+ // request hard, without an info bar to allow showing the insecure
+ // content.
+ error->DenyRequest();
+ return;
+ }
+ // We need to ask the user to approve this certificate.
+ ShowBlockingPage(this, error);
+}
+
+void SSLPolicy::OnFatalCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error) {
+ if (error->resource_type() != ResourceType::MAIN_FRAME) {
+ error->DenyRequest();
+ return;
+ }
+ error->CancelRequest();
+ ShowErrorPage(this, error);
+ // No need to degrade our security indicators because we didn't continue.
+}
diff --git a/chrome/browser/ssl/ssl_policy.h b/chrome/browser/ssl/ssl_policy.h
new file mode 100644
index 0000000..d317b63
--- /dev/null
+++ b/chrome/browser/ssl/ssl_policy.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_SSL_POLICY_H__
+#define CHROME_BROWSER_SSL_POLICY_H__
+
+#include "chrome/browser/ssl/ssl_blocking_page.h"
+#include "chrome/browser/ssl/ssl_manager.h"
+
+// The basic SSLPolicy. This class contains default implementations of all
+// the SSLPolicy entry points. It is expected that subclasses will override
+// most of these methods to implement policy specific to certain errors or
+// situations.
+class SSLPolicy : public SSLManager::Delegate,
+ public SSLBlockingPage::Delegate {
+ public:
+ // Factory method to get the default policy.
+ //
+ // SSLPolicy is not meant to be instantiated itself. Only subclasses should
+ // be instantiated. The default policy has more complex behavior than a
+ // direct instance of SSLPolicy.
+ static SSLPolicy* GetDefaultPolicy();
+
+ // SSLManager::Delegate methods.
+ virtual void OnCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error);
+ virtual void OnMixedContent(
+ NavigationController* navigation_controller,
+ const GURL& main_frame_url,
+ SSLManager::MixedContentHandler* mixed_content_handler) {
+ // So far only the default policy is expected to receive mixed-content
+ // calls.
+ NOTREACHED();
+ }
+
+ virtual void OnRequestStarted(SSLManager* manager,
+ const GURL& url,
+ ResourceType::Type resource_type,
+ int ssl_cert_id,
+ int ssl_cert_status);
+ virtual SecurityStyle GetDefaultStyle(const GURL& url);
+
+ // SSLBlockingPage::Delegate methods.
+ virtual SSLErrorInfo GetSSLErrorInfo(SSLManager::CertError* error);
+ virtual void OnDenyCertificate(SSLManager::CertError* error);
+ virtual void OnAllowCertificate(SSLManager::CertError* error);
+
+ protected:
+ // Allow our subclasses to construct us.
+ SSLPolicy();
+
+ // Helper method for derived classes handling certificate errors that can be
+ // overridden by the user.
+ // Show a blocking page and let the user continue or cancel the request.
+ void OnOverridableCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error);
+
+ // Helper method for derived classes handling fatal certificate errors.
+ // Cancel the request and show an error page.
+ void OnFatalCertError(const GURL& main_frame_url,
+ SSLManager::CertError* error);
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(SSLPolicy);
+};
+
+#endif // CHROME_BROWSER_SSL_POLICY_H__
+
diff --git a/chrome/browser/ssl/ssl_uitest.cc b/chrome/browser/ssl/ssl_uitest.cc
new file mode 100644
index 0000000..2a84744
--- /dev/null
+++ b/chrome/browser/ssl/ssl_uitest.cc
@@ -0,0 +1,932 @@
+// Copyright (c) 2006-2008 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 <Windows.h>
+#include <Wincrypt.h>
+
+#include <string>
+
+#include "chrome/common/pref_names.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "net/base/ssl_test_util.h"
+#include "net/url_request/url_request_unittest.h"
+
+namespace {
+
+const wchar_t kDocRoot[] = L"chrome/test/data";
+
+class SSLUITest : public UITest {
+ protected:
+ SSLUITest() {
+ dom_automation_enabled_ = true;
+ EXPECT_TRUE(util_.CheckCATrusted());
+ }
+
+ TabProxy* GetActiveTabProxy() {
+ scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ EXPECT_TRUE(browser_proxy.get());
+ return browser_proxy->GetActiveTab();
+ }
+
+ void NavigateTab(TabProxy* tab_proxy, const GURL& url) {
+ ASSERT_TRUE(tab_proxy->NavigateToURL(url));
+ }
+
+ void AppendTab(const GURL& url) {
+ scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ EXPECT_TRUE(browser_proxy.get());
+ EXPECT_TRUE(browser_proxy->AppendTab(url));
+ }
+
+ HTTPTestServer* PlainServer() {
+ return HTTPTestServer::CreateServer(kDocRoot);
+ }
+
+ HTTPSTestServer* GoodCertServer() {
+ return HTTPSTestServer::CreateServer(util_.kHostName, util_.kOKHTTPSPort,
+ kDocRoot, util_.GetOKCertPath().ToWStringHack());
+ }
+
+ HTTPSTestServer* BadCertServer() {
+ return HTTPSTestServer::CreateServer(util_.kHostName, util_.kBadHTTPSPort,
+ kDocRoot, util_.GetExpiredCertPath().ToWStringHack());
+ }
+
+ protected:
+ SSLTestUtil util_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLUITest);
+};
+
+} // namespace
+
+// Visits a regular page over http.
+TEST_F(SSLUITest, TestHTTP) {
+ scoped_refptr<HTTPTestServer> server = PlainServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(), server->TestServerPageW(L"files/ssl/google.html"));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visits a page over http which includes broken https resources (status should
+// be OK).
+TEST_F(SSLUITest, TestHTTPWithBrokenHTTPSResource) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+
+ NavigateTab(tab.get(),
+ http_server->TestServerPageW(L"files/ssl/page_with_unsafe_contents.html"));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visits a page over OK https:
+TEST_F(SSLUITest, TestOKHTTPS) {
+ scoped_refptr<HTTPSTestServer> https_server = GoodCertServer();
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ https_server->TestServerPageW(L"files/ssl/google.html"));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visits a page with https error and proceed:
+TEST_F(SSLUITest, TestHTTPSExpiredCertAndProceed) {
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ bad_https_server->TestServerPageW(L"files/ssl/google.html"));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::INTERSTITIAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visits a page with https error and don't proceed (and ensure we can still
+// navigate at that point):
+TEST_F(SSLUITest, TestHTTPSExpiredCertAndDontProceed) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+
+ // First navigate to an OK page.
+ NavigateTab(tab.get(),
+ good_https_server->TestServerPageW(L"files/ssl/google.html"));
+
+ GURL cross_site_url =
+ bad_https_server->TestServerPageW(L"files/ssl/google.html");
+ // Change the host name from 127.0.0.1 to localhost so it triggers a
+ // cross-site navigation so we can test http://crbug.com/5800 is gone.
+ ASSERT_EQ("127.0.0.1", cross_site_url.host());
+ GURL::Replacements replacements;
+ std::string new_host("localhost");
+ replacements.SetHostStr(new_host);
+ cross_site_url = cross_site_url.ReplaceComponents(replacements);
+
+ // Now go to a bad HTTPS page.
+ NavigateTab(tab.get(), cross_site_url);
+
+ // An interstitial should be showing.
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::INTERSTITIAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Simulate user clicking "Take me back".
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(false));
+
+ // We should be back to the original good page.
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Try to navigate to a new page. (to make sure bug 5800 is fixed).
+ NavigateTab(tab.get(),
+ http_server->TestServerPageW(L"files/ssl/google.html"));
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+}
+
+//
+// Mixed contents
+//
+
+// Visits a page with mixed content.
+TEST_F(SSLUITest, TestMixedContents) {
+ scoped_refptr<HTTPSTestServer> https_server = GoodCertServer();
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+
+ // Load a page with mixed-content, the default behavior is to show the mixed
+ // content.
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ https_server->TestServerPageW(L"files/ssl/page_with_mixed_contents.html"));
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state);
+
+ // Now select the block mixed-content pref and reload the page.
+ scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ EXPECT_TRUE(browser_proxy.get());
+ EXPECT_TRUE(browser_proxy->SetIntPreference(prefs::kMixedContentFiltering,
+ FilterPolicy::FILTER_ALL));
+ EXPECT_TRUE(tab->Reload());
+
+ // The image should be filtered.
+ int img_width;
+ EXPECT_TRUE(tab->ExecuteAndExtractInt(L"",
+ L"window.domAutomationController.send(ImageWidth());",
+ &img_width));
+ // In order to check that the image was not loaded, we check its width.
+ // The actual image (Google logo) is 114 pixels wide, we assume the broken
+ // image is less than 100.
+ EXPECT_GT(100, img_width);
+
+ // The state should be OK since we are not showing the resource.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // There should be one info-bar to show the mixed-content.
+ int info_bar_count = 0;
+ EXPECT_TRUE(tab->GetSSLInfoBarCount(&info_bar_count));
+ EXPECT_EQ(1, info_bar_count);
+
+ // Activate the link on the info-bar to show the mixed-content.
+ EXPECT_TRUE(tab->ClickSSLInfoBarLink(0, true));
+
+ // The image should show now.
+ EXPECT_TRUE(tab->ExecuteAndExtractInt(L"",
+ L"window.domAutomationController.send(ImageWidth());",
+ &img_width));
+ EXPECT_LT(100, img_width);
+
+ // And our status should be mixed-content.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state);
+}
+
+// Visits a page with unsafe content and make sure that:
+// - frames content is replaced with warning
+// - images and scripts are filtered out entirely
+TEST_F(SSLUITest, TestUnsafeContents) {
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ good_https_server->TestServerPageW(
+ L"files/ssl/page_with_unsafe_contents.html"));
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ // When the bad content is filtered, the state is expected to be
+ // authenticated.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Because of cross-frame scripting restrictions, we cannot access the iframe
+ // content. So to know if the frame was loaded, we just check if a popup was
+ // opened (the iframe content opens one).
+ // Note: because of bug 1115868, no constrained window is opened right now.
+ // Once the bug is fixed, this will do the real check.
+ int constrained_window_count = 0;
+ EXPECT_TRUE(tab->GetConstrainedWindowCount(&constrained_window_count));
+ EXPECT_EQ(0, constrained_window_count);
+
+ int img_width;
+ EXPECT_TRUE(tab->ExecuteAndExtractInt(L"",
+ L"window.domAutomationController.send(ImageWidth());",
+ &img_width));
+ // In order to check that the image was not loaded, we check its width.
+ // The actual image (Google logo) is 114 pixels wide, we assume the broken
+ // image is less than 100.
+ EXPECT_GT(100, img_width);
+
+ bool js_result = false;
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(IsFooSet());",
+ &js_result));
+ EXPECT_FALSE(js_result);
+}
+
+// Visits a page with mixed content loaded by JS (after the initial page load).
+TEST_F(SSLUITest, TestMixedContentsLoadedFromJS) {
+ scoped_refptr<HTTPSTestServer> https_server = GoodCertServer();
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(), https_server->TestServerPageW(
+ L"files/ssl/page_with_dynamic_mixed_contents.html"));
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Load the insecure image.
+ bool js_result = false;
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"loadBadImage();",
+ &js_result));
+ EXPECT_TRUE(js_result);
+
+ // We should now have mixed-contents.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state);
+}
+
+// Visits a page with an image over http. Visits another page over https
+// referencing that same image over http (hoping it is coming from the webcore
+// memory cache).
+TEST_F(SSLUITest, TestCachedMixedContents) {
+ scoped_refptr<HTTPSTestServer> https_server = GoodCertServer();
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(), http_server->TestServerPageW(
+ L"files/ssl/page_with_mixed_contents.html"));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Load again but over SSL. It should have mixed-contents (even though the
+ // image comes from the WebCore memory cache).
+ NavigateTab(tab.get(), https_server->TestServerPageW(
+ L"files/ssl/page_with_mixed_contents.html"));
+
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state);
+}
+
+// This test ensures the CN invalid status does not 'stick' to a certificate
+// (see bug #1044942) and that it depends on the host-name.
+// TODO(jcampan): this test is flacky and fails sometimes (bug #1065095)
+TEST_F(SSLUITest, DISABLED_TestCNInvalidStickiness) {
+ const std::string kLocalHost = "localhost";
+ scoped_refptr<HTTPSTestServer> https_server =
+ HTTPSTestServer::CreateServer(kLocalHost, util_.kOKHTTPSPort,
+ kDocRoot, util_.GetOKCertPath().ToWStringHack());
+ ASSERT_TRUE(NULL != https_server.get());
+
+ // First we hit the server with hostname, this generates an invalid policy
+ // error.
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(), https_server->TestServerPageW(
+ L"files/ssl/google.html"));
+
+ // We get an interstitial page as a result.
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::INTERSTITIAL_PAGE, page_type);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_COMMON_NAME_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // We proceed through the interstitial page.
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+
+ // Now we try again with the right host name this time.
+
+ // Let's change the host-name in the url.
+ GURL url = https_server->TestServerPageW(L"files/ssl/google.html");
+ std::string::size_type hostname_index = url.spec().find(kLocalHost);
+ ASSERT_TRUE(hostname_index != std::string::npos); // Test sanity check.
+ std::string new_url;
+ new_url.append(url.spec().substr(0, hostname_index));
+ new_url.append(util_.kHostName);
+ new_url.append(url.spec().substr(hostname_index + kLocalHost.size()));
+
+ NavigateTab(tab.get(), GURL(new_url));
+
+ // Security state should be OK.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Now try again the broken one to make sure it is still broken.
+ NavigateTab(tab.get(), https_server->TestServerPageW(
+ L"files/ssl/google.html"));
+
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ // Since we OKed the interstitial last time, we get right to the page.
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_COMMON_NAME_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Test that navigating to a #ref does not change a bad security state.
+TEST_F(SSLUITest, TestRefNavigation) {
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ bad_https_server->TestServerPageW(L"files/ssl/page_with_refs.html"));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(page_type, NavigationEntry::INTERSTITIAL_PAGE);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID, cert_status);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type);
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Now navigate to a ref in the page.
+ NavigateTab(tab.get(),
+ bad_https_server->TestServerPageW(L"files/ssl/page_with_refs.html#jp"));
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Tests that closing a page that has a unsafe pop-up does not crash the browser
+// (bug #1966).
+// Disabled because flaky (bug #2136).
+TEST_F(SSLUITest, DISABLED_TestCloseTabWithUnsafePopup) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ http_server->TestServerPageW(
+ L"files/ssl/page_with_unsafe_popup.html"));
+
+ int popup_count = 0;
+ EXPECT_TRUE(tab->GetConstrainedWindowCount(&popup_count));
+ EXPECT_EQ(1, popup_count);
+
+ // Let's add another tab to make sure the browser does not exit when we close
+ // the first tab.
+ scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ EXPECT_TRUE(browser_proxy.get());
+ browser_proxy->AppendTab(
+ http_server->TestServerPageW(L"files/ssl/google.html"));
+
+ // Close the first tab.
+ tab->Close();
+}
+
+// Visit a page over bad https that is a redirect to a page with good https.
+TEST_F(SSLUITest, TestRedirectBadToGoodHTTPS) {
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ GURL url1 = bad_https_server->TestServerPageW(L"server-redirect?");
+ GURL url2 = good_https_server->TestServerPageW(L"files/ssl/google.html");
+ NavigateTab(tab.get(), GURL(url1.spec() + url2.spec()));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(page_type, NavigationEntry::INTERSTITIAL_PAGE);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID, cert_status);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+ // We have been redirected to the good page.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0,
+ cert_status & net::CERT_STATUS_ALL_ERRORS); // No errors expected.
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visit a page over good https that is a redirect to a page with bad https.
+TEST_F(SSLUITest, TestRedirectGoodToBadHTTPS) {
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ GURL url1 = good_https_server->TestServerPageW(L"server-redirect?");
+ GURL url2 = bad_https_server->TestServerPageW(L"files/ssl/google.html");
+ NavigateTab(tab.get(), GURL(url1.spec() + url2.spec()));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(page_type, NavigationEntry::INTERSTITIAL_PAGE);
+
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visit a page over http that is a redirect to a page with https (good and
+// bad).
+TEST_F(SSLUITest, TestRedirectHTTPToHTTPS) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ // HTTP redirects to good HTTPS.
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ GURL http_url = http_server->TestServerPageW(L"server-redirect?");
+ GURL good_https_url =
+ good_https_server->TestServerPageW(L"files/ssl/google.html");
+ NavigateTab(tab.get(), GURL(http_url.spec() + good_https_url.spec()));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // HTTP redirects to bad HTTPS.
+ GURL bad_https_url =
+ bad_https_server->TestServerPageW(L"files/ssl/google.html");
+ NavigateTab(tab.get(), GURL(http_url.spec() + bad_https_url.spec()));
+
+ NavigationEntry::PageType page_type;
+ EXPECT_TRUE(tab->GetPageType(&page_type));
+ EXPECT_EQ(page_type, NavigationEntry::INTERSTITIAL_PAGE);
+
+ // Continue on the interstitial.
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visit a page over https that is a redirect to a page with http (to make sure
+// we don't keep the secure state).
+TEST_F(SSLUITest, TestRedirectHTTPSToHTTP) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> https_server = GoodCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ GURL https_url = https_server->TestServerPageW(L"server-redirect?");
+ GURL http_url = http_server->TestServerPageW(L"files/ssl/google.html");
+ NavigateTab(tab.get(), GURL(https_url.spec() + http_url.spec()));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// Visits a page to which we could not connect (bad port) over http and https
+// and make sure the security style is correct.
+TEST_F(SSLUITest, TestConnectToBadPort) {
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+
+ GURL http_url("http://localhost:17");
+ NavigateTab(tab.get(), http_url);
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Same thing over HTTPS.
+ GURL https_url("https://localhost:17");
+ NavigateTab(tab.get(), https_url);
+
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+//
+// Frame navigation
+//
+
+// From a good HTTPS top frame:
+// - navigate to an OK HTTPS frame
+// - navigate to a bad HTTPS (expect unsafe content and filtered frame), then
+// back
+// - navigate to HTTP (expect mixed content), then back
+TEST_F(SSLUITest, TestGoodFrameNavigation) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ good_https_server->TestServerPageW(L"files/ssl/top_frame.html"));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ bool success = false;
+ // Now navigate inside the frame.
+ int64 last_nav_time = 0;
+ EXPECT_TRUE(tab->GetLastNavigationTime(&last_nav_time));
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(clickLink('goodHTTPSLink'));",
+ &success));
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(tab->WaitForNavigation(last_nav_time));
+
+ // We should still be fine.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Now let's hit a bad page.
+ EXPECT_TRUE(tab->GetLastNavigationTime(&last_nav_time));
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(clickLink('badHTTPSLink'));",
+ &success));
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(tab->WaitForNavigation(last_nav_time));
+
+ // The security style should still be secure.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // And the frame should be blocked.
+ bool is_content_evil = true;
+ std::wstring content_frame_xpath(L"html/frameset/frame[2]");
+ std::wstring is_frame_evil_js(
+ L"window.domAutomationController"
+ L".send(document.getElementById('evilDiv') != null);");
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(content_frame_xpath,
+ is_frame_evil_js,
+ &is_content_evil));
+ EXPECT_FALSE(is_content_evil);
+
+ // Now go back, our state should return to OK.
+ EXPECT_TRUE(tab->GoBack());
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Navigate to a page served over HTTP.
+ EXPECT_TRUE(tab->GetLastNavigationTime(&last_nav_time));
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(clickLink('HTTPLink'));",
+ &success));
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(tab->WaitForNavigation(last_nav_time));
+
+ // Our state should be mixed-content.
+ // Status should be "contains bad contents".
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state);
+
+ // Go back, our state should be back to OK.
+ EXPECT_TRUE(tab->GoBack());
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// From a bad HTTPS top frame:
+// - navigate to an OK HTTPS frame (expected to be still authentication broken).
+TEST_F(SSLUITest, TestBadFrameNavigation) {
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ bad_https_server->TestServerPageW(L"files/ssl/top_frame.html"));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Continue on the interstitial.
+ EXPECT_TRUE(tab->TakeActionOnSSLBlockingPage(true));
+
+ // Navigate to a good frame.
+ bool success = false;
+ int64 last_nav_time = 0;
+ EXPECT_TRUE(tab->GetLastNavigationTime(&last_nav_time));
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(clickLink('goodHTTPSLink'));",
+ &success));
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(tab->WaitForNavigation(last_nav_time));
+
+ // We should still be authentication broken.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_AUTHENTICATION_BROKEN, security_style);
+ EXPECT_EQ(net::CERT_STATUS_DATE_INVALID,
+ cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+}
+
+// From an HTTP top frame, navigate to good and bad HTTPS (security state should
+// stay unauthenticated).
+TEST_F(SSLUITest, TestUnauthenticatedFrameNavigation) {
+ scoped_refptr<HTTPTestServer> http_server = PlainServer();
+ scoped_refptr<HTTPSTestServer> good_https_server = GoodCertServer();
+ scoped_refptr<HTTPSTestServer> bad_https_server = BadCertServer();
+
+ scoped_ptr<TabProxy> tab(GetActiveTabProxy());
+ NavigateTab(tab.get(),
+ http_server->TestServerPageW(L"files/ssl/top_frame.html"));
+
+ SecurityStyle security_style;
+ int cert_status;
+ int mixed_content_state;
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Now navigate inside the frame to a secure HTTPS frame.
+ bool success = false;
+ int64 last_nav_time = 0;
+ EXPECT_TRUE(tab->GetLastNavigationTime(&last_nav_time));
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(clickLink('goodHTTPSLink'));",
+ &success));
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(tab->WaitForNavigation(last_nav_time));
+
+ // We should still be unauthenticated.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // Now navigate to a bad HTTPS frame.
+ EXPECT_TRUE(tab->GetLastNavigationTime(&last_nav_time));
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(clickLink('badHTTPSLink'));",
+ &success));
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(tab->WaitForNavigation(last_nav_time));
+
+ // State should not have changed.
+ EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status,
+ &mixed_content_state));
+ EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, security_style);
+ EXPECT_EQ(0, cert_status & net::CERT_STATUS_ALL_ERRORS);
+ EXPECT_EQ(NavigationEntry::SSLStatus::NORMAL_CONTENT, mixed_content_state);
+
+ // And the frame should have been blocked (see bug #2316).
+ bool is_content_evil = true;
+ std::wstring content_frame_xpath(L"html/frameset/frame[2]");
+ std::wstring is_frame_evil_js(
+ L"window.domAutomationController"
+ L".send(document.getElementById('evilDiv') != null);");
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(content_frame_xpath,
+ is_frame_evil_js,
+ &is_content_evil));
+ EXPECT_FALSE(is_content_evil);
+}
+
+
+// TODO(jcampan): more tests to do below.
+
+// Visit a page over https that contains a frame with a redirect.
+
+// XMLHttpRequest mixed in synchronous mode.
+
+// XMLHttpRequest mixed in asynchronous mode.
+
+// XMLHttpRequest over bad ssl in synchronous mode.
+
+// XMLHttpRequest over OK ssl in synchronous mode.