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