// 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 #include #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 { 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_; 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 [] and |ca_name| // to something like: // "Verified by " 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 visible_info_bars_; // Handles registering notifications with the NotificationService. NotificationRegistrar registrar_; // Certificate policies for each host. std::map cert_policy_for_host_; // Domains for which it is OK to show insecure content. std::set 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 pending_messages_; DISALLOW_COPY_AND_ASSIGN(SSLManager); }; #endif // CHROME_BROWSER_SSL_MANAGER_H_