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_client_auth_handler.h"
6
7 #include "chrome/browser/ssl/ssl_client_auth_notification_details.h"
8 #include "content/browser/browser_thread.h"
9 #include "content/browser/renderer_host/render_view_host_delegate.h"
10 #include "content/browser/renderer_host/render_view_host_notification_task.h"
11 #include "content/browser/renderer_host/resource_dispatcher_host.h"
12 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
13 #include "content/common/notification_service.h"
14 #include "net/url_request/url_request.h"
15
SSLClientAuthHandler(net::URLRequest * request,net::SSLCertRequestInfo * cert_request_info)16 SSLClientAuthHandler::SSLClientAuthHandler(
17 net::URLRequest* request,
18 net::SSLCertRequestInfo* cert_request_info)
19 : request_(request),
20 cert_request_info_(cert_request_info) {
21 }
22
~SSLClientAuthHandler()23 SSLClientAuthHandler::~SSLClientAuthHandler() {
24 // If we were simply dropped, then act as if we selected no certificate.
25 DoCertificateSelected(NULL);
26 }
27
OnRequestCancelled()28 void SSLClientAuthHandler::OnRequestCancelled() {
29 request_ = NULL;
30 }
31
SelectCertificate()32 void SSLClientAuthHandler::SelectCertificate() {
33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
34
35 int render_process_host_id;
36 int render_view_host_id;
37 if (!ResourceDispatcherHost::RenderViewForRequest(request_,
38 &render_process_host_id,
39 &render_view_host_id))
40 NOTREACHED();
41
42 // If the RVH does not exist by the time this task gets run, then the task
43 // will be dropped and the scoped_refptr to SSLClientAuthHandler will go
44 // away, so we do not leak anything. The destructor takes care of ensuring
45 // the net::URLRequest always gets a response.
46 CallRenderViewHostSSLDelegate(
47 render_process_host_id, render_view_host_id,
48 &RenderViewHostDelegate::SSL::ShowClientCertificateRequestDialog,
49 scoped_refptr<SSLClientAuthHandler>(this));
50 }
51
52 // Sends an SSL_CLIENT_AUTH_CERT_SELECTED notification and notifies the IO
53 // thread that we have selected a cert.
CertificateSelected(net::X509Certificate * cert)54 void SSLClientAuthHandler::CertificateSelected(net::X509Certificate* cert) {
55 VLOG(1) << this << " CertificateSelected " << cert;
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
57
58 SSLClientAuthNotificationDetails details(cert_request_info_, cert);
59 NotificationService* service = NotificationService::current();
60 service->Notify(NotificationType::SSL_CLIENT_AUTH_CERT_SELECTED,
61 Source<SSLClientAuthHandler>(this),
62 Details<SSLClientAuthNotificationDetails>(&details));
63
64 CertificateSelectedNoNotify(cert);
65 }
66
67 // Notifies the IO thread that we have selected a cert.
CertificateSelectedNoNotify(net::X509Certificate * cert)68 void SSLClientAuthHandler::CertificateSelectedNoNotify(
69 net::X509Certificate* cert) {
70 VLOG(1) << this << " CertificateSelectedNoNotify " << cert;
71 BrowserThread::PostTask(
72 BrowserThread::IO, FROM_HERE,
73 NewRunnableMethod(
74 this, &SSLClientAuthHandler::DoCertificateSelected, cert));
75 }
76
DoCertificateSelected(net::X509Certificate * cert)77 void SSLClientAuthHandler::DoCertificateSelected(net::X509Certificate* cert) {
78 VLOG(1) << this << " DoCertificateSelected " << cert;
79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
80 // request_ could have been NULLed if the request was cancelled while the
81 // user was choosing a cert, or because we have already responded to the
82 // certificate.
83 if (request_) {
84 request_->ContinueWithCertificate(cert);
85
86 ResourceDispatcherHostRequestInfo* info =
87 ResourceDispatcherHost::InfoForRequest(request_);
88 if (info)
89 info->set_ssl_client_auth_handler(NULL);
90
91 request_ = NULL;
92 }
93 }
94
SSLClientAuthObserver(net::SSLCertRequestInfo * cert_request_info,SSLClientAuthHandler * handler)95 SSLClientAuthObserver::SSLClientAuthObserver(
96 net::SSLCertRequestInfo* cert_request_info,
97 SSLClientAuthHandler* handler)
98 : cert_request_info_(cert_request_info), handler_(handler) {
99 }
100
~SSLClientAuthObserver()101 SSLClientAuthObserver::~SSLClientAuthObserver() {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
103 }
104
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)105 void SSLClientAuthObserver::Observe(
106 NotificationType type,
107 const NotificationSource& source,
108 const NotificationDetails& details) {
109 VLOG(1) << "SSLClientAuthObserver::Observe " << this << " " << handler_.get();
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
111 DCHECK(type == NotificationType::SSL_CLIENT_AUTH_CERT_SELECTED);
112
113 if (Source<SSLClientAuthHandler>(source).ptr() == handler_.get()) {
114 VLOG(1) << "got notification from ourself " << handler_.get();
115 return;
116 }
117
118 SSLClientAuthNotificationDetails* auth_details =
119 Details<SSLClientAuthNotificationDetails>(details).ptr();
120 if (!auth_details->IsSameHost(cert_request_info_))
121 return;
122
123 VLOG(1) << this << " got matching notification for "
124 << handler_.get() << ", selecting cert "
125 << auth_details->selected_cert();
126 StopObserving();
127 handler_->CertificateSelectedNoNotify(auth_details->selected_cert());
128 OnCertSelectedByNotification();
129 }
130
StartObserving()131 void SSLClientAuthObserver::StartObserving() {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133 notification_registrar_.Add(this,
134 NotificationType::SSL_CLIENT_AUTH_CERT_SELECTED,
135 NotificationService::AllSources());
136 }
137
StopObserving()138 void SSLClientAuthObserver::StopObserving() {
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140 notification_registrar_.RemoveAll();
141 }
142