blob: 0906c365a7d77dfa57624a304054f110b850a2e1 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/certificate_provider/certificate_provider_service.h"
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_piece.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/certificate_provider/certificate_provider.h"
#include "net/base/net_errors.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#endif // IS_CHROMEOS_LACROS
namespace chromeos {
namespace {
void PostSignResult(net::SSLPrivateKey::SignCallback callback,
net::Error error,
const std::vector<uint8_t>& signature) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), error, signature));
}
void PostIdentities(
base::OnceCallback<void(net::ClientCertIdentityList)> callback,
net::ClientCertIdentityList certs) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(certs)));
}
} // namespace
class CertificateProviderService::CertificateProviderImpl
: public CertificateProvider {
public:
// This provider must be used on the same thread as the
// CertificateProviderService.
explicit CertificateProviderImpl(
const base::WeakPtr<CertificateProviderService>& service);
CertificateProviderImpl(const CertificateProviderImpl&) = delete;
CertificateProviderImpl& operator=(const CertificateProviderImpl&) = delete;
~CertificateProviderImpl() override;
void GetCertificates(
base::OnceCallback<void(net::ClientCertIdentityList)> callback) override;
private:
const base::WeakPtr<CertificateProviderService> service_;
SEQUENCE_CHECKER(sequence_checker_);
};
// Implements an SSLPrivateKey backed by the signing function exposed by an
// extension through the certificateProvider API.
// Objects of this class must be used the CertificateProviderService's sequence.
class CertificateProviderService::SSLPrivateKey : public net::SSLPrivateKey {
public:
SSLPrivateKey(const std::string& extension_id,
const CertificateInfo& cert_info,
const base::WeakPtr<CertificateProviderService>& service);
SSLPrivateKey(const SSLPrivateKey&) = delete;
SSLPrivateKey& operator=(const SSLPrivateKey&) = delete;
// net::SSLPrivateKey:
std::string GetProviderName() override;
std::vector<uint16_t> GetAlgorithmPreferences() override;
void Sign(uint16_t algorithm,
base::span<const uint8_t> input,
SignCallback callback) override;
private:
~SSLPrivateKey() override;
const std::string extension_id_;
const CertificateInfo cert_info_;
const base::WeakPtr<CertificateProviderService> service_;
SEQUENCE_CHECKER(sequence_checker_);
};
class CertificateProviderService::ClientCertIdentity
: public net::ClientCertIdentity {
public:
ClientCertIdentity(scoped_refptr<net::X509Certificate> cert,
base::WeakPtr<CertificateProviderService> service)
: net::ClientCertIdentity(std::move(cert)), service_(service) {}
ClientCertIdentity(const ClientCertIdentity&) = delete;
ClientCertIdentity& operator=(const ClientCertIdentity&) = delete;
~ClientCertIdentity() override;
void AcquirePrivateKey(
base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
private_key_callback) override;
private:
SEQUENCE_CHECKER(sequence_checker_);
const base::WeakPtr<CertificateProviderService> service_;
};
CertificateProviderService::ClientCertIdentity::~ClientCertIdentity() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CertificateProviderService::ClientCertIdentity::AcquirePrivateKey(
base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
private_key_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!service_) {
std::move(private_key_callback).Run(nullptr);
return;
}
bool is_currently_provided = false;
CertificateInfo info;
std::string extension_id;
// TODO(mattm): can the ClientCertIdentity store a handle directly to the
// extension instead of having to go through service_->certificate_map_ ?
service_->certificate_map_.LookUpCertificate(
*certificate(), &is_currently_provided, &info, &extension_id);
if (!is_currently_provided) {
std::move(private_key_callback).Run(nullptr);
return;
}
std::move(private_key_callback)
.Run(base::MakeRefCounted<SSLPrivateKey>(extension_id, info, service_));
}
CertificateProviderService::CertificateProviderImpl::CertificateProviderImpl(
const base::WeakPtr<CertificateProviderService>& service)
: service_(service) {}
CertificateProviderService::CertificateProviderImpl::
~CertificateProviderImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CertificateProviderService::CertificateProviderImpl::GetCertificates(
base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Avoid running the callback reentrantly.
callback = base::BindOnce(&PostIdentities, std::move(callback));
if (!service_) {
std::move(callback).Run(net::ClientCertIdentityList());
return;
}
service_->GetCertificatesFromExtensions(std::move(callback));
}
CertificateProviderService::SSLPrivateKey::SSLPrivateKey(
const std::string& extension_id,
const CertificateInfo& cert_info,
const base::WeakPtr<CertificateProviderService>& service)
: extension_id_(extension_id), cert_info_(cert_info), service_(service) {}
std::string CertificateProviderService::SSLPrivateKey::GetProviderName() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return "extension \"" + extension_id_ + "\"";
}
std::vector<uint16_t>
CertificateProviderService::SSLPrivateKey::GetAlgorithmPreferences() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return cert_info_.supported_algorithms;
}
void CertificateProviderService::SSLPrivateKey::Sign(
uint16_t algorithm,
base::span<const uint8_t> input,
SignCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The Sign() method should not call its callback reentrantly, so wrap it in
// PostSignResult().
callback = base::BindOnce(&PostSignResult, std::move(callback));
if (!service_) {
std::move(callback).Run(net::ERR_FAILED, /*signature=*/{});
return;
}
service_->RequestSignatureFromExtension(
extension_id_, cert_info_.certificate, algorithm, input,
/*authenticating_user_account_id=*/{}, std::move(callback));
}
CertificateProviderService::SSLPrivateKey::~SSLPrivateKey() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
CertificateProviderService::CertificateProviderService() {}
CertificateProviderService::~CertificateProviderService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CertificateProviderService::SetDelegate(
std::unique_ptr<Delegate> delegate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!delegate_);
DCHECK(delegate);
delegate_ = std::move(delegate);
}
void CertificateProviderService::AddObserver(Observer* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.AddObserver(observer);
}
void CertificateProviderService::RemoveObserver(Observer* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.RemoveObserver(observer);
}
void CertificateProviderService::SetCertificatesProvidedByExtension(
const std::string& extension_id,
const CertificateInfoList& certificate_infos) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Synchronize with Ash-Chrome
chromeos::LacrosService* service = chromeos::LacrosService::Get();
if (service && service->IsAvailable<crosapi::mojom::CertDatabase>() &&
service->GetInterfaceVersion(crosapi::mojom::CertDatabase::Uuid_) >=
static_cast<int>(crosapi::mojom::CertDatabase::MethodMinVersions::
kSetCertsProvidedByExtensionMinVersion)) {
service->GetRemote<crosapi::mojom::CertDatabase>()
->SetCertsProvidedByExtension(extension_id, certificate_infos);
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
certificate_map_.UpdateCertificatesForExtension(extension_id,
certificate_infos);
for (auto& observer : observers_)
observer.OnCertificatesUpdated(extension_id, certificate_infos);
}
bool CertificateProviderService::SetExtensionCertificateReplyReceived(
const std::string& extension_id,
int cert_request_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool completed = false;
if (!certificate_requests_.SetExtensionReplyReceived(
extension_id, cert_request_id, &completed)) {
DLOG(WARNING) << "Unexpected reply of extension " << extension_id
<< " to request " << cert_request_id;
return false;
}
if (completed) {
base::OnceCallback<void(net::ClientCertIdentityList)> callback;
certificate_requests_.RemoveRequest(cert_request_id, &callback);
CollectCertificatesAndRun(std::move(callback));
}
return true;
}
bool CertificateProviderService::ReplyToSignRequest(
const std::string& extension_id,
int sign_request_id,
const std::vector<uint8_t>& signature) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
LOG(WARNING) << "Extension " << extension_id
<< " replied to signature request " << sign_request_id
<< ", size " << signature.size();
scoped_refptr<net::X509Certificate> certificate;
net::SSLPrivateKey::SignCallback callback;
if (!sign_requests_.RemoveRequest(extension_id, sign_request_id, &certificate,
&callback)) {
return false;
}
pin_dialog_manager_.RemoveSignRequest(extension_id, sign_request_id);
const net::Error error_code = signature.empty() ? net::ERR_FAILED : net::OK;
std::move(callback).Run(error_code, signature);
if (!signature.empty()) {
for (auto& observer : observers_)
observer.OnSignCompleted(certificate, extension_id);
}
return true;
}
bool CertificateProviderService::LookUpCertificate(
const net::X509Certificate& cert,
bool* has_extension,
std::string* extension_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CertificateInfo unused_info;
return certificate_map_.LookUpCertificate(cert, has_extension, &unused_info,
extension_id);
}
std::unique_ptr<CertificateProvider>
CertificateProviderService::CreateCertificateProvider() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<CertificateProviderImpl>(weak_factory_.GetWeakPtr());
}
void CertificateProviderService::OnExtensionUnregistered(
const std::string& extension_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
certificate_map_.RemoveExtension(extension_id);
}
void CertificateProviderService::OnExtensionUnloaded(
const std::string& extension_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
certificate_map_.RemoveExtension(extension_id);
for (const int completed_cert_request_id :
certificate_requests_.DropExtension(extension_id)) {
base::OnceCallback<void(net::ClientCertIdentityList)> callback;
certificate_requests_.RemoveRequest(completed_cert_request_id, &callback);
CollectCertificatesAndRun(std::move(callback));
}
for (auto& callback : sign_requests_.RemoveAllRequests(extension_id))
std::move(callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
pin_dialog_manager_.ExtensionUnloaded(extension_id);
}
void CertificateProviderService::RequestSignatureBySpki(
const std::string& subject_public_key_info,
uint16_t algorithm,
base::span<const uint8_t> input,
const absl::optional<AccountId>& authenticating_user_account_id,
net::SSLPrivateKey::SignCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool is_currently_provided = false;
CertificateInfo info;
std::string extension_id;
certificate_map_.LookUpCertificateBySpki(
subject_public_key_info, &is_currently_provided, &info, &extension_id);
if (!is_currently_provided) {
LOG(ERROR) << "no certificate with the specified spki was found";
std::move(callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
return;
}
RequestSignatureFromExtension(extension_id, info.certificate, algorithm,
input, authenticating_user_account_id,
std::move(callback));
}
bool CertificateProviderService::LookUpSpki(
const std::string& subject_public_key_info,
std::vector<uint16_t>* supported_algorithms,
std::string* extension_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool is_currently_provided = false;
CertificateInfo info;
certificate_map_.LookUpCertificateBySpki(
subject_public_key_info, &is_currently_provided, &info, extension_id);
if (!is_currently_provided) {
LOG(ERROR) << "no certificate with the specified spki was found";
return false;
}
*supported_algorithms = info.supported_algorithms;
return true;
}
void CertificateProviderService::AbortSignatureRequestsForAuthenticatingUser(
const AccountId& authenticating_user_account_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
using ExtensionNameRequestIdPair =
certificate_provider::SignRequests::ExtensionNameRequestIdPair;
const std::vector<ExtensionNameRequestIdPair> sign_requests_to_abort =
sign_requests_.FindRequestsForAuthenticatingUser(
authenticating_user_account_id);
for (const ExtensionNameRequestIdPair& sign_request :
sign_requests_to_abort) {
const std::string& extension_id = sign_request.first;
const int sign_request_id = sign_request.second;
// TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
LOG(WARNING) << "Aborting user login signature request from extension "
<< extension_id << " id " << sign_request_id;
pin_dialog_manager_.RemoveSignRequest(extension_id, sign_request_id);
scoped_refptr<net::X509Certificate> certificate;
net::SSLPrivateKey::SignCallback sign_callback;
if (sign_requests_.RemoveRequest(extension_id, sign_request_id,
&certificate, &sign_callback)) {
std::move(sign_callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
}
}
}
void CertificateProviderService::GetCertificatesFromExtensions(
base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const std::vector<std::string> provider_extensions(
delegate_->CertificateProviderExtensions());
if (provider_extensions.empty()) {
DVLOG(2) << "No provider extensions left.";
// Note that there could still be unfinished requests to extensions that
// were previously registered.
CollectCertificatesAndRun(std::move(callback));
return;
}
const int cert_request_id = certificate_requests_.AddRequest(
provider_extensions, std::move(callback),
base::BindOnce(&CertificateProviderService::TerminateCertificateRequest,
base::Unretained(this)));
DVLOG(2) << "Start certificate request " << cert_request_id;
delegate_->BroadcastCertificateRequest(cert_request_id);
}
void CertificateProviderService::CollectCertificatesAndRun(
base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
net::ClientCertIdentityList client_cert_identity_list;
std::vector<scoped_refptr<net::X509Certificate>> certificates =
certificate_map_.GetCertificates();
for (const scoped_refptr<net::X509Certificate>& certificate : certificates) {
client_cert_identity_list.push_back(std::make_unique<ClientCertIdentity>(
certificate, weak_factory_.GetWeakPtr()));
}
std::move(callback).Run(std::move(client_cert_identity_list));
}
void CertificateProviderService::TerminateCertificateRequest(
int cert_request_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::OnceCallback<void(net::ClientCertIdentityList)> callback;
if (!certificate_requests_.RemoveRequest(cert_request_id, &callback)) {
DLOG(WARNING) << "Request id " << cert_request_id << " unknown.";
return;
}
DVLOG(1) << "Time out certificate request " << cert_request_id;
CollectCertificatesAndRun(std::move(callback));
}
void CertificateProviderService::RequestSignatureFromExtension(
const std::string& extension_id,
const scoped_refptr<net::X509Certificate>& certificate,
uint16_t algorithm,
base::span<const uint8_t> input,
const absl::optional<AccountId>& authenticating_user_account_id,
net::SSLPrivateKey::SignCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const int sign_request_id = sign_requests_.AddRequest(
extension_id, certificate, authenticating_user_account_id,
std::move(callback));
// TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
LOG(WARNING) << "Starting signature request to extension " << extension_id
<< " id " << sign_request_id;
pin_dialog_manager_.AddSignRequestId(extension_id, sign_request_id,
authenticating_user_account_id);
if (!delegate_->DispatchSignRequestToExtension(
extension_id, sign_request_id, algorithm, certificate, input)) {
// TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
LOG(WARNING) << "Failed to dispatch signature request to extension "
<< extension_id << " id " << sign_request_id;
scoped_refptr<net::X509Certificate> local_certificate;
sign_requests_.RemoveRequest(extension_id, sign_request_id,
&local_certificate, &callback);
pin_dialog_manager_.RemoveSignRequest(extension_id, sign_request_id);
std::move(callback).Run(net::ERR_FAILED, std::vector<uint8_t>());
}
}
} // namespace chromeos