diff options
author | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-20 01:37:47 +0000 |
---|---|---|
committer | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-20 01:37:47 +0000 |
commit | e4182160e5cfb36d623ca3f7baef71ea4fd980af (patch) | |
tree | c59e127afa23ac826c61c01b6899fb0ac3911c5f /chrome | |
parent | 1ccd006ccad33e8d4480435c81d04d0706d63ca7 (diff) | |
download | chromium_src-e4182160e5cfb36d623ca3f7baef71ea4fd980af.zip chromium_src-e4182160e5cfb36d623ca3f7baef71ea4fd980af.tar.gz chromium_src-e4182160e5cfb36d623ca3f7baef71ea4fd980af.tar.bz2 |
SSLPolicy fix: Step 9 of 9 (hopefully!).
Change our algorithm for computing the state of our SSL security indicators. Previously, we were computing this state for a single navigation entry. Although this matches other browsers, it fails to take the same-origin policy into account. For example, if one tab is contaminated with insecure content, that insecure content can spread to all the tabs in the same security origin.
R=jcampan,wtc
BUG=8706
TEST=SSLUITest.TestMixedContentsRandomizeHash,SSLUITest.TestMixedContentsTwoTabs
Review URL: http://codereview.chromium.org/42314
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12178 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/renderer_host/resource_request_details.h | 3 | ||||
-rwxr-xr-x | chrome/browser/ssl/ssl_host_state.cc | 12 | ||||
-rwxr-xr-x | chrome/browser/ssl/ssl_host_state.h | 4 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_manager.cc | 88 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_manager.h | 20 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_policy.cc | 207 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_policy.h | 5 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_uitest.cc | 102 | ||||
-rw-r--r-- | chrome/browser/tab_contents/navigation_controller.cc | 5 | ||||
-rw-r--r-- | chrome/browser/tab_contents/navigation_entry.h | 8 | ||||
-rw-r--r-- | chrome/test/data/ssl/blank_page.html | 8 | ||||
-rwxr-xr-x | chrome/test/data/ssl/page_with_http_script.html | 8 | ||||
-rwxr-xr-x | chrome/test/data/ssl/randomize_hash.js | 2 |
13 files changed, 270 insertions, 202 deletions
diff --git a/chrome/browser/renderer_host/resource_request_details.h b/chrome/browser/renderer_host/resource_request_details.h index 8c2b98a..0e0ac17 100644 --- a/chrome/browser/renderer_host/resource_request_details.h +++ b/chrome/browser/renderer_host/resource_request_details.h @@ -44,6 +44,7 @@ class ResourceRequestDetails { resource_type_ = info->resource_type; frame_origin_ = info->frame_origin; main_frame_origin_ = info->main_frame_origin; + filter_policy_ = info->filter_policy; } ~ResourceRequestDetails() { } @@ -61,6 +62,7 @@ class ResourceRequestDetails { int ssl_cert_id() const { return ssl_cert_id_; } int ssl_cert_status() const { return ssl_cert_status_; } ResourceType::Type resource_type() const { return resource_type_; } + FilterPolicy::Type filter_policy() const { return filter_policy_; } private: GURL url_; @@ -76,6 +78,7 @@ class ResourceRequestDetails { int ssl_cert_id_; int ssl_cert_status_; ResourceType::Type resource_type_; + FilterPolicy::Type filter_policy_; DISALLOW_COPY_AND_ASSIGN(ResourceRequestDetails); }; diff --git a/chrome/browser/ssl/ssl_host_state.cc b/chrome/browser/ssl/ssl_host_state.cc index 5a5b7ae..f8a063b 100755 --- a/chrome/browser/ssl/ssl_host_state.cc +++ b/chrome/browser/ssl/ssl_host_state.cc @@ -56,18 +56,6 @@ void SSLHostState::AllowCertForHost(net::X509Certificate* cert, cert_policy_for_host_[host].Allow(cert); } -bool SSLHostState::DidAllowCertForHost(const std::string& host) { - DCHECK(CalledOnValidThread()); - - std::map<std::string, net::X509Certificate::Policy>::const_iterator iter = - cert_policy_for_host_.find(host); - - if (iter == cert_policy_for_host_.end()) - return false; - - return iter->second.HasAllowedCert(); -} - net::X509Certificate::Policy::Judgment SSLHostState::QueryPolicy( net::X509Certificate* cert, const std::string& host) { DCHECK(CalledOnValidThread()); diff --git a/chrome/browser/ssl/ssl_host_state.h b/chrome/browser/ssl/ssl_host_state.h index d8cbeff..f66e9bc 100755 --- a/chrome/browser/ssl/ssl_host_state.h +++ b/chrome/browser/ssl/ssl_host_state.h @@ -41,10 +41,6 @@ class SSLHostState : public NonThreadSafe { // 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 there is at least one certificate that has been manually - // allowed for this host. - bool DidAllowCertForHost(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); diff --git a/chrome/browser/ssl/ssl_manager.cc b/chrome/browser/ssl/ssl_manager.cc index f158104..461979d 100644 --- a/chrome/browser/ssl/ssl_manager.cc +++ b/chrome/browser/ssl/ssl_manager.cc @@ -206,14 +206,12 @@ void SSLManager::DenyCertForHost(net::X509Certificate* cert, const std::string& host) { // Remember that we don't like this cert for this host. ssl_host_state_->DenyCertForHost(cert, host); - DispatchSSLInternalStateChanged(); } // Delegate API method. void SSLManager::AllowCertForHost(net::X509Certificate* cert, const std::string& host) { ssl_host_state_->AllowCertForHost(cert, host); - DispatchSSLInternalStateChanged(); } // Delegate API method. @@ -225,7 +223,6 @@ net::X509Certificate::Policy::Judgment SSLManager::QueryPolicy( // Delegate API method. void SSLManager::AllowMixedContentForHost(const std::string& host) { ssl_host_state_->AllowMixedContentForHost(host); - DispatchSSLInternalStateChanged(); } // Delegate API method. @@ -506,7 +503,8 @@ bool SSLManager::ShouldStartRequest(ResourceDispatcherHost* rdh, // to respond synchronously to avoid delaying all network requests... if (!SSLPolicy::IsMixedContent(request->url(), info->resource_type, - info->main_frame_origin)) + info->filter_policy, + info->frame_origin)) return true; @@ -573,24 +571,18 @@ void SSLManager::DispatchSSLVisibleStateChanged() { NotificationService::NoDetails()); } -void SSLManager::InitializeEntryIfNeeded(NavigationEntry* entry) { - DCHECK(entry); +void SSLManager::UpdateEntry(NavigationEntry* entry) { + // We don't always have a navigation entry to update, for example in the + // case of the Web Inspector. + if (!entry) + return; - // 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())); - } -} + NavigationEntry::SSLStatus original_ssl_status = entry->ssl(); // Copy! -void SSLManager::NavigationStateChanged() { - NavigationEntry* active_entry = controller_->GetActiveEntry(); - if (!active_entry) - return; // Nothing showing yet. + delegate()->UpdateEntry(this, entry); - // This might be a new entry we've never seen before. - InitializeEntryIfNeeded(active_entry); + if (!entry->ssl().Equals(original_ssl_status)) + DispatchSSLVisibleStateChanged(); } void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) { @@ -599,12 +591,15 @@ void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* 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. + // This resource must have been loaded with FilterPolicy::DONT_FILTER because + // filtered resouces aren't cachable. scoped_refptr<RequestInfo> info = new RequestInfo( this, details->url(), ResourceType::SUB_RESOURCE, details->frame_origin(), details->main_frame_origin(), + FilterPolicy::DONT_FILTER, details->ssl_cert_id(), details->ssl_cert_status()); @@ -622,62 +617,28 @@ void SSLManager::DidCommitProvisionalLoad( 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); + NavigationEntry* entry = controller_->GetActiveEntry(); - bool changed = false; if (details->is_main_frame) { - // Update the SSL states of the pending entry. - NavigationEntry* entry = controller_->GetActiveEntry(); if (entry) { + // 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); + // 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) && - !details->is_content_filtered) { - 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( - NotificationType::SSL_VISIBLE_STATE_CHANGED, - Source<NavigationController>(controller_), - NotificationService::NoDetails()); - } + UpdateEntry(entry); } void SSLManager::DidFailProvisionalLoadWithError( @@ -701,6 +662,7 @@ void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) { details->resource_type(), details->frame_origin(), details->main_frame_origin(), + details->filter_policy(), details->ssl_cert_id(), details->ssl_cert_status()); @@ -728,7 +690,7 @@ void SSLManager::ShowPendingMessages() { } void SSLManager::DidChangeSSLInternalState() { - // TODO(abarth): We'll need to do something here in the next step. + UpdateEntry(controller_->GetActiveEntry()); } void SSLManager::ClearPendingMessages() { diff --git a/chrome/browser/ssl/ssl_manager.h b/chrome/browser/ssl/ssl_manager.h index b15f7e8..b72f1377f 100644 --- a/chrome/browser/ssl/ssl_manager.h +++ b/chrome/browser/ssl/ssl_manager.h @@ -270,6 +270,7 @@ class SSLManager : public NotificationObserver { ResourceType::Type resource_type, const std::string& frame_origin, const std::string& main_frame_origin, + FilterPolicy::Type filter_policy, int ssl_cert_id, int ssl_cert_status) : manager_(manager), @@ -277,6 +278,7 @@ class SSLManager : public NotificationObserver { resource_type_(resource_type), frame_origin_(frame_origin), main_frame_origin_(main_frame_origin), + filter_policy_(filter_policy), ssl_cert_id_(ssl_cert_id), ssl_cert_status_(ssl_cert_status) { } @@ -286,6 +288,7 @@ class SSLManager : public NotificationObserver { ResourceType::Type resource_type() const { return resource_type_; } const std::string& frame_origin() const { return frame_origin_; } const std::string& main_frame_origin() const { return main_frame_origin_; } + FilterPolicy::Type filter_policy() const { return filter_policy_; } int ssl_cert_id() const { return ssl_cert_id_; } int ssl_cert_status() const { return ssl_cert_status_; } @@ -295,6 +298,7 @@ class SSLManager : public NotificationObserver { ResourceType::Type resource_type_; std::string frame_origin_; std::string main_frame_origin_; + FilterPolicy::Type filter_policy_; int ssl_cert_id_; int ssl_cert_status_; @@ -323,8 +327,8 @@ class SSLManager : public NotificationObserver { // We have started a resource request with the given info. virtual void OnRequestStarted(RequestInfo* info) = 0; - // Returns the default security style for a given URL. - virtual SecurityStyle GetDefaultStyle(const GURL& url) = 0; + // Update the SSL information in |entry| to match the current state. + virtual void UpdateEntry(SSLManager* manager, NavigationEntry* entry) = 0; }; static void RegisterUserPrefs(PrefService* prefs); @@ -441,15 +445,11 @@ class SSLManager : public NotificationObserver { 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; + // The navigation controller associated with this SSLManager. The + // NavigationController is guaranteed to outlive the SSLManager. NavigationController* controller() { return controller_; } // Convenience methods for serializing/deserializing the security info. @@ -510,8 +510,8 @@ class SSLManager : public NotificationObserver { // Dispatch NotificationType::SSL_VISIBLE_STATE_CHANGED notification. void DispatchSSLVisibleStateChanged(); - // Convenience method for initializing navigation entries. - void InitializeEntryIfNeeded(NavigationEntry* entry); + // Update the NavigationEntry with our current state. + void UpdateEntry(NavigationEntry* entry); // Shows the pending messages (in info-bars) if any. void ShowPendingMessages(); diff --git a/chrome/browser/ssl/ssl_policy.cc b/chrome/browser/ssl/ssl_policy.cc index ed9678b..9e796fa 100644 --- a/chrome/browser/ssl/ssl_policy.cc +++ b/chrome/browser/ssl/ssl_policy.cc @@ -38,6 +38,44 @@ // Wrap all these helper classes in an anonymous namespace. namespace { +static void MarkOriginAsBroken(SSLManager* manager, const std::string& origin) { + GURL parsed_origin(origin); + if (!parsed_origin.SchemeIsSecure()) + return; + + manager->MarkHostAsBroken(parsed_origin.host()); +} + +static void AllowMixedContentForOrigin(SSLManager* manager, + const std::string& origin) { + GURL parsed_origin(origin); + if (!parsed_origin.SchemeIsSecure()) + return; + + manager->AllowMixedContentForHost(parsed_origin.host()); +} + +static void UpdateStateForMixedContent(SSLManager::RequestInfo* info) { + if (info->resource_type() != ResourceType::MAIN_FRAME || + info->resource_type() != ResourceType::SUB_FRAME) { + // The frame's origin now contains mixed content and therefore is broken. + MarkOriginAsBroken(info->manager(), info->frame_origin()); + } + + if (info->resource_type() != ResourceType::MAIN_FRAME) { + // The main frame now contains a frame with mixed content. Therefore, we + // mark the main frame's origin as broken too. + MarkOriginAsBroken(info->manager(), info->main_frame_origin()); + } +} + +static void UpdateStateForUnsafeContent(SSLManager::RequestInfo* info) { + // This request as a broken cert, which means its host is broken. + info->manager()->MarkHostAsBroken(info->url().host()); + + UpdateStateForMixedContent(info); +} + class ShowMixedContentTask : public Task { public: ShowMixedContentTask(SSLManager::MixedContentHandler* handler); @@ -60,10 +98,9 @@ ShowMixedContentTask::~ShowMixedContentTask() { } void ShowMixedContentTask::Run() { - handler_->manager()->AllowMixedContentForHost( - GURL(handler_->main_frame_origin()).host()); - - // Reload the page. + AllowMixedContentForOrigin(handler_->manager(), handler_->frame_origin()); + AllowMixedContentForOrigin(handler_->manager(), + handler_->main_frame_origin()); handler_->manager()->controller()->Reload(true); } @@ -112,6 +149,23 @@ static void ShowBlockingPage(SSLPolicy* policy, SSLManager::CertError* error) { blocking_page->Show(); } +static void InitializeEntryIfNeeded(NavigationEntry* entry) { + if (entry->ssl().security_style() != SECURITY_STYLE_UNKNOWN) + return; + + entry->ssl().set_security_style(entry->url().SchemeIsSecure() ? + SECURITY_STYLE_AUTHENTICATED : SECURITY_STYLE_UNAUTHENTICATED); +} + +static void AddMixedContentWarningToConsole( + SSLManager::MixedContentHandler* handler) { + const std::wstring& msg = l10n_util::GetStringF( + IDS_MIXED_CONTENT_LOG_MESSAGE, + UTF8ToWide(handler->frame_origin()), + UTF8ToWide(handler->request_url().spec())); + handler->manager()->AddMessageToConsole(msg, MESSAGE_LEVEL_WARNING); +} + } // namespace SSLPolicy::SSLPolicy() { @@ -128,15 +182,6 @@ void SSLPolicy::OnCertError(SSLManager::CertError* error) { error->request_url().host()); if (judgment == net::X509Certificate::Policy::ALLOWED) { - // We've been told to allow this certificate. - if (error->manager()->SetMaxSecurityStyle( - SECURITY_STYLE_AUTHENTICATION_BROKEN)) { - NotificationService::current()->Notify( - NotificationType::SSL_VISIBLE_STATE_CHANGED, - Source<NavigationController>(error->manager()->controller()), - Details<NavigationEntry>( - error->manager()->controller()->GetActiveEntry())); - } error->ContinueRequest(); return; } @@ -179,9 +224,10 @@ void SSLPolicy::OnMixedContent(SSLManager::MixedContentHandler* handler) { FilterPolicy::Type filter_policy = FilterPolicy::FromInt(prefs->GetInteger(prefs::kMixedContentFiltering)); - // If the user have added an exception, doctor the |filter_policy|. - if (handler->manager()->DidAllowMixedContentForHost( - GURL(handler->main_frame_origin()).host())) + // If the user has added an exception, doctor the |filter_policy|. + std::string host = GURL(handler->main_frame_origin()).host(); + if (handler->manager()->DidAllowMixedContentForHost(host) || + handler->manager()->DidMarkHostAsBroken(host)) filter_policy = FilterPolicy::DONT_FILTER; if (filter_policy != FilterPolicy::DONT_FILTER) { @@ -190,108 +236,52 @@ void SSLPolicy::OnMixedContent(SSLManager::MixedContentHandler* handler) { l10n_util::GetString(IDS_SSL_INFO_BAR_SHOW_CONTENT), new ShowMixedContentTask(handler)); } - handler->StartRequest(filter_policy); - NavigationEntry* entry = - handler->manager()->controller()->GetLastCommittedEntry(); - // 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; + handler->StartRequest(filter_policy); + AddMixedContentWarningToConsole(handler); +} - // 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(); +void SSLPolicy::OnRequestStarted(SSLManager::RequestInfo* info) { + if (net::IsCertStatusError(info->ssl_cert_status())) + UpdateStateForUnsafeContent(info); + + if (IsMixedContent(info->url(), + info->resource_type(), + info->filter_policy(), + info->frame_origin())) + UpdateStateForMixedContent(info); +} - // 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(handler->request_url().spec())); - handler->manager()->AddMessageToConsole(msg, MESSAGE_LEVEL_WARNING); +void SSLPolicy::UpdateEntry(SSLManager* manager, NavigationEntry* entry) { + DCHECK(entry); - NotificationService::current()->Notify( - NotificationType::SSL_VISIBLE_STATE_CHANGED, - Source<NavigationController>(handler->manager()->controller()), - Details<NavigationEntry>(entry)); -} + InitializeEntryIfNeeded(entry); -void SSLPolicy::OnRequestStarted(SSLManager::RequestInfo* info) { - // These schemes never leave the browser and don't require a warning. - if (info->url().SchemeIs(chrome::kDataScheme) || - info->url().SchemeIs(chrome::kJavaScriptScheme) || - info->url().SchemeIs(chrome::kAboutScheme)) + if (!entry->url().SchemeIsSecure()) return; - NavigationEntry* entry = info->manager()->controller()->GetActiveEntry(); - if (!entry) { - // We may not have an entry for cases such as the inspector. + // 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 (!entry->ssl().cert_id()) { + entry->ssl().set_security_style(SECURITY_STYLE_UNAUTHENTICATED); return; } - NavigationEntry::SSLStatus& ssl = entry->ssl(); - bool changed = false; - if (!entry->url().SchemeIsSecure() || // Current page is not secure. - info->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. + if (net::IsCertStatusError(entry->ssl().cert_status())) { + entry->ssl().set_security_style(SECURITY_STYLE_AUTHENTICATION_BROKEN); return; } - if (info->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(info->ssl_cert_status())) { - if (net::IsCertStatusError(info->ssl_cert_status())) { - // The resource is unsafe. - if (!ssl.has_unsafe_content()) { - changed = true; - ssl.set_has_unsafe_content(); - info->manager()->SetMaxSecurityStyle( - SECURITY_STYLE_AUTHENTICATION_BROKEN); - } - } - } - - if (changed) { - // Only send the notification when something actually changed. - NotificationService::current()->Notify( - NotificationType::SSL_VISIBLE_STATE_CHANGED, - Source<NavigationController>(info->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; + if (manager->DidMarkHostAsBroken(entry->url().host())) + entry->ssl().set_has_mixed_content(); } // static bool SSLPolicy::IsMixedContent(const GURL& url, ResourceType::Type resource_type, - const std::string& main_frame_origin) { + FilterPolicy::Type filter_policy, + const std::string& frame_origin) { //////////////////////////////////////////////////////////////////////////// // WARNING: This function is called from both the IO and UI threads. Do // // not touch any non-thread-safe objects! You have been warned. // @@ -301,9 +291,20 @@ bool SSLPolicy::IsMixedContent(const GURL& url, if (resource_type == ResourceType::MAIN_FRAME) return false; - // TODO(abarth): This is wrong, but it matches our current behavior. - // I'll fix this in a subsequent step. - return GURL(main_frame_origin).SchemeIsSecure() && !url.SchemeIsSecure(); + // If we've filtered the resource, then it's no longer dangerous. + if (filter_policy != FilterPolicy::DONT_FILTER) + return false; + + // If the frame doing the loading is already insecure, then we must have + // already dealt with whatever mixed content might be going on. + if (!GURL(frame_origin).SchemeIsSecure()) + return false; + + // We aren't worried about mixed content if we're loading an HTTPS URL. + if (url.SchemeIsSecure()) + return false; + + return true; } //////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/ssl/ssl_policy.h b/chrome/browser/ssl/ssl_policy.h index 0a017eb..51a3a3a 100644 --- a/chrome/browser/ssl/ssl_policy.h +++ b/chrome/browser/ssl/ssl_policy.h @@ -25,13 +25,14 @@ class SSLPolicy : public SSLManager::Delegate, virtual void OnCertError(SSLManager::CertError* error); virtual void OnMixedContent(SSLManager::MixedContentHandler* handler); virtual void OnRequestStarted(SSLManager::RequestInfo* info); - virtual SecurityStyle GetDefaultStyle(const GURL& url); + virtual void UpdateEntry(SSLManager* manager, NavigationEntry* entry); // This method is static because it is called from both the UI and the IO // threads. static bool IsMixedContent(const GURL& url, ResourceType::Type resource_type, - const std::string& main_frame_origin); + FilterPolicy::Type filter_policy, + const std::string& frame_origin); // SSLBlockingPage::Delegate methods. virtual SSLErrorInfo GetSSLErrorInfo(SSLManager::CertError* error); diff --git a/chrome/browser/ssl/ssl_uitest.cc b/chrome/browser/ssl/ssl_uitest.cc index 7ba3b61..5275b92 100644 --- a/chrome/browser/ssl/ssl_uitest.cc +++ b/chrome/browser/ssl/ssl_uitest.cc @@ -242,13 +242,29 @@ TEST_F(SSLUITest, TestMixedContents) { 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 mixed content. +TEST_F(SSLUITest, TestMixedContentsFilterAll) { + scoped_refptr<HTTPSTestServer> https_server = GoodCertServer(); + scoped_refptr<HTTPTestServer> http_server = PlainServer(); // 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()); + + // Load a page with mixed-content, we've overridden our filtering policy so + // we won't load the mixed content by default. + 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); // The image should be filtered. int img_width; @@ -260,6 +276,9 @@ TEST_F(SSLUITest, TestMixedContents) { // image is less than 100. EXPECT_GT(100, img_width); + SecurityStyle security_style; + int cert_status; + int mixed_content_state; // The state should be OK since we are not showing the resource. EXPECT_TRUE(tab->GetSecurityState(&security_style, &cert_status, &mixed_content_state)); @@ -289,6 +308,32 @@ TEST_F(SSLUITest, TestMixedContents) { EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state); } +// Visits a page with an http script that tries to suppress our mixed content +// warnings by randomize location.hash. +// Based on http://crbug.com/8706 +TEST_F(SSLUITest, TestMixedContentsRandomizeHash) { + 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_http_script.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::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 @@ -378,6 +423,57 @@ TEST_F(SSLUITest, TestMixedContentsLoadedFromJS) { EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state); } +// Visits two pages from the same origin: one with mixed content and one +// without. The test checks that we propagate the mixed content state from one +// to the other. +TEST_F(SSLUITest, TestMixedContentsTwoTabs) { + scoped_refptr<HTTPSTestServer> https_server = GoodCertServer(); + scoped_refptr<HTTPTestServer> http_server = PlainServer(); + + scoped_ptr<TabProxy> tab1(GetActiveTabProxy()); + NavigateTab( + tab1.get(), + https_server->TestServerPageW( + L"files/ssl/blank_page.html")); + NavigationEntry::PageType page_type; + EXPECT_TRUE(tab1->GetPageType(&page_type)); + EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type); + + // This tab should be fine. + SecurityStyle security_style; + int cert_status; + int mixed_content_state; + EXPECT_TRUE(tab1->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); + + scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0)); + EXPECT_TRUE(browser_proxy.get()); + EXPECT_TRUE(browser_proxy->AppendTab( + https_server->TestServerPageW(L"files/ssl/page_with_http_script.html"))); + + scoped_ptr<TabProxy> tab2(GetActiveTabProxy()); + EXPECT_TRUE(tab2->GetPageType(&page_type)); + EXPECT_EQ(NavigationEntry::NORMAL_PAGE, page_type); + + // The new tab has mixed content. + EXPECT_TRUE(tab2->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); + + // Which means the origin for the first tab has also been contaminated with + // mixed content. + EXPECT_TRUE(tab1->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 an image over http. Visits another page over https // referencing that same image over http (hoping it is coming from the webcore // memory cache). @@ -803,13 +899,13 @@ TEST_F(SSLUITest, TestGoodFrameNavigation) { 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. + // Go back, our state should be unchanged. 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); + EXPECT_EQ(NavigationEntry::SSLStatus::MIXED_CONTENT, mixed_content_state); } // From a bad HTTPS top frame: diff --git a/chrome/browser/tab_contents/navigation_controller.cc b/chrome/browser/tab_contents/navigation_controller.cc index 38dafb6d..3dbdef8 100644 --- a/chrome/browser/tab_contents/navigation_controller.cc +++ b/chrome/browser/tab_contents/navigation_controller.cc @@ -1011,10 +1011,6 @@ void NavigationController::NavigateToPendingEntry(bool reload) { pending_entry_ = entries_[pending_entry_index_].get(); } - // Reset the security states as any SSL error may have been resolved since we - // last visited that page. - pending_entry_->ssl() = NavigationEntry::SSLStatus(); - if (from_contents && from_contents->type() != pending_entry_->tab_type()) from_contents->set_is_active(false); @@ -1036,7 +1032,6 @@ void NavigationController::NotifyNavigationEntryCommitted( // TODO(pkasting): http://b/1113079 Probably these explicit notification paths // should be removed, and interested parties should just listen for the // notification below instead. - ssl_manager_.NavigationStateChanged(); active_contents_->NotifyNavigationStateChanged( TabContents::INVALIDATE_EVERYTHING); diff --git a/chrome/browser/tab_contents/navigation_entry.h b/chrome/browser/tab_contents/navigation_entry.h index 48e1750..16801b3 100644 --- a/chrome/browser/tab_contents/navigation_entry.h +++ b/chrome/browser/tab_contents/navigation_entry.h @@ -45,6 +45,14 @@ class NavigationEntry { SSLStatus(); + bool Equals(const SSLStatus& status) const { + return security_style_ == status.security_style_ && + cert_id_ == status.cert_id_ && + cert_status_ == status.cert_status_ && + security_bits_ == status.security_bits_ && + content_status_ == status.content_status_; + } + void set_security_style(SecurityStyle security_style) { security_style_ = security_style; } diff --git a/chrome/test/data/ssl/blank_page.html b/chrome/test/data/ssl/blank_page.html new file mode 100644 index 0000000..68b761a --- /dev/null +++ b/chrome/test/data/ssl/blank_page.html @@ -0,0 +1,8 @@ +<html> +<head> +<title>I am a blank page.</title> +</head> +<body> +Nothing to see here. +</body> +</html> diff --git a/chrome/test/data/ssl/page_with_http_script.html b/chrome/test/data/ssl/page_with_http_script.html new file mode 100755 index 0000000..d0719c4 --- /dev/null +++ b/chrome/test/data/ssl/page_with_http_script.html @@ -0,0 +1,8 @@ +<html> +<head><title>Page with http script</title></head> +<body> +This page contains an script which is served over an http connection, +causing mixed contents (when this page is loaded over https).<br> +<script src="http://localhost:1337/files/ssl/randomize_hash.js"></script> +</body> +</html> diff --git a/chrome/test/data/ssl/randomize_hash.js b/chrome/test/data/ssl/randomize_hash.js new file mode 100755 index 0000000..46c2082 --- /dev/null +++ b/chrome/test/data/ssl/randomize_hash.js @@ -0,0 +1,2 @@ +document.body.innerHTML = "I'm content from an HTTP script." +location.hash = Math.random(); |