ssl_manager.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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 "app/l10n_util.h" 8#include "base/utf_string_conversions.h" 9#include "chrome/browser/browser_thread.h" 10#include "chrome/browser/load_from_memory_cache_details.h" 11#include "chrome/browser/net/url_request_tracking.h" 12#include "chrome/browser/prefs/pref_service.h" 13#include "chrome/browser/renderer_host/resource_request_details.h" 14#include "chrome/browser/ssl/ssl_cert_error_handler.h" 15#include "chrome/browser/ssl/ssl_policy.h" 16#include "chrome/browser/ssl/ssl_request_info.h" 17#include "chrome/browser/tab_contents/navigation_controller.h" 18#include "chrome/browser/tab_contents/navigation_entry.h" 19#include "chrome/browser/tab_contents/provisional_load_details.h" 20#include "chrome/common/notification_service.h" 21#include "chrome/common/pref_names.h" 22#include "grit/generated_resources.h" 23#include "net/base/cert_status_flags.h" 24 25// static 26void SSLManager::OnSSLCertificateError(ResourceDispatcherHost* rdh, 27 net::URLRequest* request, 28 int cert_error, 29 net::X509Certificate* cert) { 30 DVLOG(1) << "OnSSLCertificateError() cert_error: " << cert_error 31 << " url: " << request->url().spec(); 32 33 ResourceDispatcherHostRequestInfo* info = 34 ResourceDispatcherHost::InfoForRequest(request); 35 DCHECK(info); 36 37 // A certificate error occurred. Construct a SSLCertErrorHandler object and 38 // hand it over to the UI thread for processing. 39 BrowserThread::PostTask( 40 BrowserThread::UI, FROM_HERE, 41 NewRunnableMethod(new SSLCertErrorHandler(rdh, 42 request, 43 info->resource_type(), 44 info->frame_origin(), 45 info->main_frame_origin(), 46 cert_error, 47 cert), 48 &SSLCertErrorHandler::Dispatch)); 49} 50 51// static 52void SSLManager::NotifySSLInternalStateChanged() { 53 NotificationService::current()->Notify( 54 NotificationType::SSL_INTERNAL_STATE_CHANGED, 55 NotificationService::AllSources(), 56 NotificationService::NoDetails()); 57} 58 59// static 60std::string SSLManager::SerializeSecurityInfo(int cert_id, 61 int cert_status, 62 int security_bits, 63 int ssl_connection_status) { 64 Pickle pickle; 65 pickle.WriteInt(cert_id); 66 pickle.WriteInt(cert_status); 67 pickle.WriteInt(security_bits); 68 pickle.WriteInt(ssl_connection_status); 69 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); 70} 71 72// static 73bool SSLManager::DeserializeSecurityInfo(const std::string& state, 74 int* cert_id, 75 int* cert_status, 76 int* security_bits, 77 int* ssl_connection_status) { 78 DCHECK(cert_id && cert_status && security_bits && ssl_connection_status); 79 if (state.empty()) { 80 // No SSL used. 81 *cert_id = 0; 82 // The following are not applicable and are set to the default values. 83 *cert_status = 0; 84 *security_bits = -1; 85 *ssl_connection_status = 0; 86 return false; 87 } 88 89 Pickle pickle(state.data(), static_cast<int>(state.size())); 90 void * iter = NULL; 91 return pickle.ReadInt(&iter, cert_id) && 92 pickle.ReadInt(&iter, cert_status) && 93 pickle.ReadInt(&iter, security_bits) && 94 pickle.ReadInt(&iter, ssl_connection_status); 95} 96 97// static 98std::wstring SSLManager::GetEVCertName(const net::X509Certificate& cert) { 99 // EV are required to have an organization name and country. 100 if (cert.subject().organization_names.empty() || 101 cert.subject().country_name.empty()) { 102 NOTREACHED(); 103 return std::wstring(); 104 } 105 106 return l10n_util::GetStringF(IDS_SECURE_CONNECTION_EV, 107 UTF8ToWide(cert.subject().organization_names[0]), 108 UTF8ToWide(cert.subject().country_name)); 109} 110 111SSLManager::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<NavigationController>(controller_)); 122 registrar_.Add(this, NotificationType::RESOURCE_RECEIVED_REDIRECT, 123 Source<NavigationController>(controller_)); 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 130SSLManager::~SSLManager() { 131} 132 133void 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 164void SSLManager::DidRunInsecureContent(const std::string& security_origin) { 165 policy()->DidRunInsecureContent(controller_->GetActiveEntry(), 166 security_origin); 167} 168 169bool 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 179void 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 206void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) { 207 DCHECK(details); 208 209 // Simulate loading this resource through the usual path. 210 // Note that we specify SUB_RESOURCE as the resource type as WebCore only 211 // caches sub-resources. 212 // This resource must have been loaded with no filtering because filtered 213 // resouces aren't cachable. 214 scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo( 215 details->url(), 216 ResourceType::SUB_RESOURCE, 217 details->frame_origin(), 218 details->main_frame_origin(), 219 details->pid(), 220 details->ssl_cert_id(), 221 details->ssl_cert_status())); 222 223 // Simulate loading this resource through the usual path. 224 policy()->OnRequestStarted(info.get()); 225} 226 227void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) { 228 DCHECK(details); 229 230 scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo( 231 details->url(), 232 details->resource_type(), 233 details->frame_origin(), 234 details->main_frame_origin(), 235 details->origin_child_id(), 236 details->ssl_cert_id(), 237 details->ssl_cert_status())); 238 239 // Notify our policy that we started a resource request. Ideally, the 240 // policy should have the ability to cancel the request, but we can't do 241 // that yet. 242 policy()->OnRequestStarted(info.get()); 243} 244 245void SSLManager::DidReceiveResourceRedirect(ResourceRedirectDetails* details) { 246 // TODO(abarth): Make sure our redirect behavior is correct. If we ever see a 247 // non-HTTPS resource in the redirect chain, we want to trigger 248 // insecure content, even if the redirect chain goes back to 249 // HTTPS. This is because the network attacker can redirect the 250 // HTTP request to https://attacker.com/payload.js. 251} 252 253void SSLManager::DidChangeSSLInternalState() { 254 UpdateEntry(controller_->GetActiveEntry()); 255} 256 257void SSLManager::UpdateEntry(NavigationEntry* entry) { 258 // We don't always have a navigation entry to update, for example in the 259 // case of the Web Inspector. 260 if (!entry) 261 return; 262 263 NavigationEntry::SSLStatus original_ssl_status = entry->ssl(); // Copy! 264 265 policy()->UpdateEntry(entry, controller_->tab_contents()); 266 267 if (!entry->ssl().Equals(original_ssl_status)) { 268 NotificationService::current()->Notify( 269 NotificationType::SSL_VISIBLE_STATE_CHANGED, 270 Source<NavigationController>(controller_), 271 NotificationService::NoDetails()); 272 } 273} 274