• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
OnSSLCertificateError(ResourceDispatcherHost * rdh,net::URLRequest * request,int cert_error,net::X509Certificate * cert)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
NotifySSLInternalStateChanged()51 void SSLManager::NotifySSLInternalStateChanged() {
52   NotificationService::current()->Notify(
53       NotificationType::SSL_INTERNAL_STATE_CHANGED,
54       NotificationService::AllSources(),
55       NotificationService::NoDetails());
56 }
57 
58 // static
SerializeSecurityInfo(int cert_id,int cert_status,int security_bits,int ssl_connection_status)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
DeserializeSecurityInfo(const std::string & state,int * cert_id,int * cert_status,int * security_bits,int * ssl_connection_status)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
GetEVCertName(const net::X509Certificate & cert)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 
SSLManager(NavigationController * controller)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 
~SSLManager()130 SSLManager::~SSLManager() {
131 }
132 
DidCommitProvisionalLoad(const NotificationDetails & in_details)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 
DidRunInsecureContent(const std::string & security_origin)164 void SSLManager::DidRunInsecureContent(const std::string& security_origin) {
165   policy()->DidRunInsecureContent(controller_->GetActiveEntry(),
166                                   security_origin);
167 }
168 
ProcessedSSLErrorFromRequest() const169 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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
DidLoadFromMemoryCache(LoadFromMemoryCacheDetails * details)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 
DidStartResourceResponse(ResourceRequestDetails * details)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 
DidReceiveResourceRedirect(ResourceRedirectDetails * details)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 
DidChangeSSLInternalState()245 void SSLManager::DidChangeSSLInternalState() {
246   UpdateEntry(controller_->GetActiveEntry());
247 }
248 
UpdateEntry(NavigationEntry * entry)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