Home | History | Annotate | Download | only in ssl
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ssl/ssl_manager.h"
      6 
      7 #include "base/utf_string_conversions.h"
      8 #include "chrome/browser/load_from_memory_cache_details.h"
      9 #include "chrome/browser/net/url_request_tracking.h"
     10 #include "chrome/browser/ssl/ssl_cert_error_handler.h"
     11 #include "chrome/browser/ssl/ssl_policy.h"
     12 #include "chrome/browser/ssl/ssl_request_info.h"
     13 #include "content/browser/browser_thread.h"
     14 #include "content/browser/renderer_host/resource_dispatcher_host.h"
     15 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
     16 #include "content/browser/renderer_host/resource_request_details.h"
     17 #include "content/browser/tab_contents/navigation_controller.h"
     18 #include "content/browser/tab_contents/navigation_entry.h"
     19 #include "content/browser/tab_contents/provisional_load_details.h"
     20 #include "content/browser/tab_contents/tab_contents.h"
     21 #include "content/common/notification_service.h"
     22 #include "grit/generated_resources.h"
     23 #include "net/base/cert_status_flags.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 
     26 // static
     27 void SSLManager::OnSSLCertificateError(ResourceDispatcherHost* rdh,
     28                                        net::URLRequest* request,
     29                                        int cert_error,
     30                                        net::X509Certificate* cert) {
     31   DVLOG(1) << "OnSSLCertificateError() cert_error: " << cert_error
     32            << " url: " << request->url().spec();
     33 
     34   ResourceDispatcherHostRequestInfo* info =
     35       ResourceDispatcherHost::InfoForRequest(request);
     36   DCHECK(info);
     37 
     38   // A certificate error occurred.  Construct a SSLCertErrorHandler object and
     39   // hand it over to the UI thread for processing.
     40   BrowserThread::PostTask(
     41       BrowserThread::UI, FROM_HERE,
     42       NewRunnableMethod(new SSLCertErrorHandler(rdh,
     43                                                 request,
     44                                                 info->resource_type(),
     45                                                 cert_error,
     46                                                 cert),
     47                         &SSLCertErrorHandler::Dispatch));
     48 }
     49 
     50 // static
     51 void SSLManager::NotifySSLInternalStateChanged() {
     52   NotificationService::current()->Notify(
     53       NotificationType::SSL_INTERNAL_STATE_CHANGED,
     54       NotificationService::AllSources(),
     55       NotificationService::NoDetails());
     56 }
     57 
     58 // static
     59 std::string SSLManager::SerializeSecurityInfo(int cert_id,
     60                                               int cert_status,
     61                                               int security_bits,
     62                                               int ssl_connection_status) {
     63   Pickle pickle;
     64   pickle.WriteInt(cert_id);
     65   pickle.WriteInt(cert_status);
     66   pickle.WriteInt(security_bits);
     67   pickle.WriteInt(ssl_connection_status);
     68   return std::string(static_cast<const char*>(pickle.data()), pickle.size());
     69 }
     70 
     71 // static
     72 bool SSLManager::DeserializeSecurityInfo(const std::string& state,
     73                                          int* cert_id,
     74                                          int* cert_status,
     75                                          int* security_bits,
     76                                          int* ssl_connection_status) {
     77   DCHECK(cert_id && cert_status && security_bits && ssl_connection_status);
     78   if (state.empty()) {
     79     // No SSL used.
     80     *cert_id = 0;
     81     // The following are not applicable and are set to the default values.
     82     *cert_status = 0;
     83     *security_bits = -1;
     84     *ssl_connection_status = 0;
     85     return false;
     86   }
     87 
     88   Pickle pickle(state.data(), static_cast<int>(state.size()));
     89   void * iter = NULL;
     90   return pickle.ReadInt(&iter, cert_id) &&
     91          pickle.ReadInt(&iter, cert_status) &&
     92          pickle.ReadInt(&iter, security_bits) &&
     93          pickle.ReadInt(&iter, ssl_connection_status);
     94 }
     95 
     96 // static
     97 string16 SSLManager::GetEVCertName(const net::X509Certificate& cert) {
     98   // EV are required to have an organization name and country.
     99   if (cert.subject().organization_names.empty() ||
    100       cert.subject().country_name.empty()) {
    101     NOTREACHED();
    102     return string16();
    103   }
    104 
    105   return l10n_util::GetStringFUTF16(
    106       IDS_SECURE_CONNECTION_EV,
    107       UTF8ToUTF16(cert.subject().organization_names[0]),
    108       UTF8ToUTF16(cert.subject().country_name));
    109 }
    110 
    111 SSLManager::SSLManager(NavigationController* controller)
    112     : backend_(controller),
    113       policy_(new SSLPolicy(&backend_)),
    114       controller_(controller) {
    115   DCHECK(controller_);
    116 
    117   // Subscribe to various notifications.
    118   registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
    119                  Source<NavigationController>(controller_));
    120   registrar_.Add(this, NotificationType::RESOURCE_RESPONSE_STARTED,
    121                  Source<RenderViewHostDelegate>(controller_->tab_contents()));
    122   registrar_.Add(this, NotificationType::RESOURCE_RECEIVED_REDIRECT,
    123                  Source<RenderViewHostDelegate>(controller_->tab_contents()));
    124   registrar_.Add(this, NotificationType::LOAD_FROM_MEMORY_CACHE,
    125                  Source<NavigationController>(controller_));
    126   registrar_.Add(this, NotificationType::SSL_INTERNAL_STATE_CHANGED,
    127                  NotificationService::AllSources());
    128 }
    129 
    130 SSLManager::~SSLManager() {
    131 }
    132 
    133 void SSLManager::DidCommitProvisionalLoad(
    134     const NotificationDetails& in_details) {
    135   NavigationController::LoadCommittedDetails* details =
    136       Details<NavigationController::LoadCommittedDetails>(in_details).ptr();
    137 
    138   NavigationEntry* entry = controller_->GetActiveEntry();
    139 
    140   if (details->is_main_frame) {
    141     if (entry) {
    142       // Decode the security details.
    143       int ssl_cert_id, ssl_cert_status, ssl_security_bits,
    144           ssl_connection_status;
    145       DeserializeSecurityInfo(details->serialized_security_info,
    146                               &ssl_cert_id,
    147                               &ssl_cert_status,
    148                               &ssl_security_bits,
    149                               &ssl_connection_status);
    150 
    151       // We may not have an entry if this is a navigation to an initial blank
    152       // page. Reset the SSL information and add the new data we have.
    153       entry->ssl() = NavigationEntry::SSLStatus();
    154       entry->ssl().set_cert_id(ssl_cert_id);
    155       entry->ssl().set_cert_status(ssl_cert_status);
    156       entry->ssl().set_security_bits(ssl_security_bits);
    157       entry->ssl().set_connection_status(ssl_connection_status);
    158     }
    159   }
    160 
    161   UpdateEntry(entry);
    162 }
    163 
    164 void SSLManager::DidRunInsecureContent(const std::string& security_origin) {
    165   policy()->DidRunInsecureContent(controller_->GetActiveEntry(),
    166                                   security_origin);
    167 }
    168 
    169 bool SSLManager::ProcessedSSLErrorFromRequest() const {
    170   NavigationEntry* entry = controller_->GetActiveEntry();
    171   if (!entry) {
    172     NOTREACHED();
    173     return false;
    174   }
    175 
    176   return net::IsCertStatusError(entry->ssl().cert_status());
    177 }
    178 
    179 void SSLManager::Observe(NotificationType type,
    180                          const NotificationSource& source,
    181                          const NotificationDetails& details) {
    182   // Dispatch by type.
    183   switch (type.value) {
    184     case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR:
    185       // Do nothing.
    186       break;
    187     case NotificationType::RESOURCE_RESPONSE_STARTED:
    188       DidStartResourceResponse(Details<ResourceRequestDetails>(details).ptr());
    189       break;
    190     case NotificationType::RESOURCE_RECEIVED_REDIRECT:
    191       DidReceiveResourceRedirect(
    192           Details<ResourceRedirectDetails>(details).ptr());
    193       break;
    194     case NotificationType::LOAD_FROM_MEMORY_CACHE:
    195       DidLoadFromMemoryCache(
    196           Details<LoadFromMemoryCacheDetails>(details).ptr());
    197       break;
    198     case NotificationType::SSL_INTERNAL_STATE_CHANGED:
    199       DidChangeSSLInternalState();
    200       break;
    201     default:
    202       NOTREACHED() << "The SSLManager received an unexpected notification.";
    203   }
    204 }
    205 
    206 void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) {
    207   // Simulate loading this resource through the usual path.
    208   // Note that we specify SUB_RESOURCE as the resource type as WebCore only
    209   // caches sub-resources.
    210   // This resource must have been loaded with no filtering because filtered
    211   // resouces aren't cachable.
    212   scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo(
    213       details->url(),
    214       ResourceType::SUB_RESOURCE,
    215       details->pid(),
    216       details->ssl_cert_id(),
    217       details->ssl_cert_status()));
    218 
    219   // Simulate loading this resource through the usual path.
    220   policy()->OnRequestStarted(info.get());
    221 }
    222 
    223 void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) {
    224   scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo(
    225       details->url(),
    226       details->resource_type(),
    227       details->origin_child_id(),
    228       details->ssl_cert_id(),
    229       details->ssl_cert_status()));
    230 
    231   // Notify our policy that we started a resource request.  Ideally, the
    232   // policy should have the ability to cancel the request, but we can't do
    233   // that yet.
    234   policy()->OnRequestStarted(info.get());
    235 }
    236 
    237 void SSLManager::DidReceiveResourceRedirect(ResourceRedirectDetails* details) {
    238   // TODO(abarth): Make sure our redirect behavior is correct.  If we ever see a
    239   //               non-HTTPS resource in the redirect chain, we want to trigger
    240   //               insecure content, even if the redirect chain goes back to
    241   //               HTTPS.  This is because the network attacker can redirect the
    242   //               HTTP request to https://attacker.com/payload.js.
    243 }
    244 
    245 void SSLManager::DidChangeSSLInternalState() {
    246   UpdateEntry(controller_->GetActiveEntry());
    247 }
    248 
    249 void SSLManager::UpdateEntry(NavigationEntry* entry) {
    250   // We don't always have a navigation entry to update, for example in the
    251   // case of the Web Inspector.
    252   if (!entry)
    253     return;
    254 
    255   NavigationEntry::SSLStatus original_ssl_status = entry->ssl();  // Copy!
    256 
    257   policy()->UpdateEntry(entry, controller_->tab_contents());
    258 
    259   if (!entry->ssl().Equals(original_ssl_status)) {
    260     NotificationService::current()->Notify(
    261         NotificationType::SSL_VISIBLE_STATE_CHANGED,
    262         Source<NavigationController>(controller_),
    263         NotificationService::NoDetails());
    264   }
    265 }
    266