blob: adf58f3ee3f252212b9689787750c4aae79fda57 [file] [log] [blame]
// 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/provisional_load_details.h"
#include "chrome/browser/resource_dispatcher_host.h"
#include "chrome/browser/security_style.h"
#include "chrome/browser/views/info_bar_message_view.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/render_messages.h"
#include "chrome/views/link.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 InfoBarItemView;
class NavigationEntry;
class LoadFromMemoryCacheDetails;
class LoadNotificationDetails;
class NavigationController;
class PrefService;
class ResourceRedirectDetails;
class ResourceRequestDetails;
class SSLErrorInfo;
class TabContents;
class Task;
class URLRequest;
// 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 TabContents this object is associated with. Should be
// called from the UI thread.
TabContents* GetTabContents();
// 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;
};
// An info bar with a message and an optional link that runs a task when
// clicked.
class SSLInfoBar : public InfoBarItemView,
public views::LinkController {
public:
SSLInfoBar(SSLManager* manager,
const std::wstring& message,
const std::wstring& link_text,
Task* task);
virtual ~SSLInfoBar();
const std::wstring GetMessageText() const;
// views::LinkController method.
virtual void LinkActivated(views::Link* source, int event_flags);
private:
views::Label* label_;
views::Link* link_;
SSLManager* manager_;
scoped_ptr<Task> task_;
DISALLOW_COPY_AND_ASSIGN(SSLInfoBar);
};
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 when one of our infobars closes.
void OnInfoBarClose(SSLInfoBar* info_bar);
// 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:
// The AutomationProvider needs to access the InfoBars.
friend class AutomationProvider;
// 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_;
// The list of currently visible SSL InfoBars.
ObserverList<SSLInfoBar> visible_info_bars_;
// 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_