Home | History | Annotate | Download | only in ssl
      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