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 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 23 SSLClientAuthHandler::~SSLClientAuthHandler() { 24 // If we were simply dropped, then act as if we selected no certificate. 25 DoCertificateSelected(NULL); 26 } 27 28 void SSLClientAuthHandler::OnRequestCancelled() { 29 request_ = NULL; 30 } 31 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. 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. 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 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 95 SSLClientAuthObserver::SSLClientAuthObserver( 96 net::SSLCertRequestInfo* cert_request_info, 97 SSLClientAuthHandler* handler) 98 : cert_request_info_(cert_request_info), handler_(handler) { 99 } 100 101 SSLClientAuthObserver::~SSLClientAuthObserver() { 102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 103 } 104 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 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 138 void SSLClientAuthObserver::StopObserving() { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 140 notification_registrar_.RemoveAll(); 141 } 142