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