| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ssl/ssl_client_auth_observer.h" |
| |
| #include "base/logging.h" |
| #include "base/no_destructor.h" |
| #include "chrome/browser/ssl/ssl_client_auth_metrics.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/client_certificate_delegate.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/ssl/ssl_cert_request_info.h" |
| #include "net/ssl/ssl_private_key.h" |
| |
| using content::BrowserThread; |
| |
| SSLClientAuthObserver::SSLClientAuthObserver( |
| const content::BrowserContext* browser_context, |
| const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info, |
| std::unique_ptr<content::ClientCertificateDelegate> delegate) |
| : browser_context_(browser_context), |
| cert_request_info_(cert_request_info), |
| delegate_(std::move(delegate)) {} |
| |
| SSLClientAuthObserver::~SSLClientAuthObserver() { |
| } |
| |
| void SSLClientAuthObserver::CertificateSelected( |
| net::X509Certificate* certificate, |
| net::SSLPrivateKey* private_key) { |
| if (!delegate_) |
| return; |
| |
| // CertificateSelected is called with a valid delegate any time that the |
| // selector is explicitly closed by the user. If the user closes the entire |
| // tab, CancelCertificateSelection() is called first, removing the delegate. |
| if (certificate) |
| LogClientAuthResult(ClientCertSelectionResult::kUserSelect); |
| else |
| LogClientAuthResult(ClientCertSelectionResult::kUserCancel); |
| |
| // Stop listening now that the delegate has been resolved. This is also to |
| // avoid getting a self-notification. |
| StopObserving(); |
| |
| // Iterate a copy of the observer list as other observers may remove |
| // themselves from it in their callbacks. |
| std::set<SSLClientAuthObserver*> observers_copy = GetActiveObservers(); |
| for (SSLClientAuthObserver* other_observer : observers_copy) { |
| other_observer->CertificateSelectedWithOtherObserver( |
| browser_context_, cert_request_info_.get(), certificate, private_key); |
| } |
| |
| delegate_->ContinueWithCertificate(certificate, private_key); |
| delegate_.reset(); |
| } |
| |
| void SSLClientAuthObserver::CancelCertificateSelection() { |
| if (!delegate_) |
| return; |
| |
| // This code is only reached when the selector's tab is closed-- cancelling |
| // the selection box calls CertificateSelected(nullptr, nullptr) first. |
| LogClientAuthResult(ClientCertSelectionResult::kUserCloseTab); |
| |
| // Stop observing now that the delegate has been resolved. |
| StopObserving(); |
| delegate_.reset(); |
| } |
| |
| void SSLClientAuthObserver::CertificateSelectedWithOtherObserver( |
| const content::BrowserContext* browser_context, |
| net::SSLCertRequestInfo* cert_request_info, |
| net::X509Certificate* certificate, |
| net::SSLPrivateKey* private_key) { |
| DVLOG(1) << "SSLClientAuthObserver::Observe " << this; |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (browser_context != browser_context_) |
| return; |
| |
| if (!cert_request_info->host_and_port.Equals( |
| cert_request_info_->host_and_port)) { |
| return; |
| } |
| |
| DVLOG(1) << this << " got matching notification and selecting cert " |
| << certificate; |
| StopObserving(); |
| delegate_->ContinueWithCertificate(certificate, private_key); |
| delegate_.reset(); |
| OnCertSelectedByNotification(); |
| } |
| |
| void SSLClientAuthObserver::StartObserving() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetActiveObservers().insert(this); |
| } |
| |
| void SSLClientAuthObserver::StopObserving() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetActiveObservers().erase(this); |
| } |
| |
| // static |
| std::set<SSLClientAuthObserver*>& SSLClientAuthObserver::GetActiveObservers() { |
| static base::NoDestructor<std::set<SSLClientAuthObserver*>> observers; |
| return *observers; |
| } |