#include "chrome/browser/ui/webui/certificate_manager/client_cert_sources.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.h"
#include "chrome/common/net/x509_certificate_model.h"
#include "crypto/crypto_buildflags.h"
#include "crypto/sha2.h"
#include "net/base/hash_value.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/client_cert_identity.h"
#include "net/ssl/client_cert_store.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/selected_file_info.h"
#if BUILDFLAG(USE_NSS_CERTS)
#include "chrome/browser/ui/crypto_module_delegate_nss.h"
#include "net/ssl/client_cert_store_nss.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "net/ssl/client_cert_store_win.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "net/ssl/client_cert_store_mac.h"
#endif
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#include "chrome/browser/enterprise/client_certificates/certificate_provisioning_service_factory.h"
#include "components/enterprise/client_certificates/core/certificate_provisioning_service.h"
#include "components/enterprise/client_certificates/core/client_certificates_service.h"
#include "components/enterprise/client_certificates/core/features.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/certificate_provider/certificate_provider.h"
#include "chrome/browser/certificate_provider/certificate_provider_service.h"
#include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
#endif
namespace {
class ClientCertStoreLoader { … };
std::unique_ptr<ClientCertStoreLoader> CreatePlatformClientCertLoader() { … }
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
class NullClientCertStore : public net::ClientCertStore { … };
std::unique_ptr<ClientCertStoreLoader> CreateProvisionedClientCertLoader(
Profile* profile) { … }
#endif
void PopulateCertInfosFromCertificateList(
CertificateManagerPageHandler::GetCertificatesCallback callback,
const net::CertificateList& certs) { … }
void ViewCertificateFromCertificateList(
const std::string& sha256_hex_hash,
const net::CertificateList& certs,
base::WeakPtr<content::WebContents> web_contents) { … }
class ClientCertSource : public CertificateManagerPageHandler::CertSource { … };
#if BUILDFLAG(IS_CHROMEOS_ASH)
class CrosClientCertSource : public ClientCertSource,
public ui::SelectFileDialog::Listener {
public:
explicit CrosClientCertSource(
std::unique_ptr<ClientCertStoreLoader> loader,
mojo::Remote<certificate_manager_v2::mojom::CertificateManagerPage>*
remote_client)
: ClientCertSource(std::move(loader)), remote_client_(remote_client) {}
~CrosClientCertSource() override {
if (select_file_dialog_) {
select_file_dialog_->ListenerDestroyed();
}
}
void ImportCertificate(
base::WeakPtr<content::WebContents> web_contents,
CertificateManagerPageHandler::ImportCertificateCallback callback)
override {
if (!web_contents || select_file_dialog_) {
std::move(callback).Run(nullptr);
return;
}
import_callback_ = std::move(callback);
select_file_dialog_ = ui::SelectFileDialog::Create(
this, std::make_unique<ChromeSelectFilePolicy>(web_contents.get()));
ui::SelectFileDialog::FileTypeInfo file_type_info;
file_type_info.extensions = {{FILE_PATH_LITERAL("p12"),
FILE_PATH_LITERAL("pfx"),
FILE_PATH_LITERAL("crt")}};
file_type_info.include_all_files = true;
select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_OPEN_FILE, std::u16string(),
base::FilePath(), &file_type_info,
1,
FILE_PATH_LITERAL("p12"), web_contents->GetTopLevelNativeWindow(),
nullptr);
}
void FileSelected(const ui::SelectedFileInfo& file, int index) override {
select_file_dialog_ = nullptr;
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&base::ReadFileToBytes, file.path()),
base::BindOnce(&CrosClientCertSource::FileRead,
weak_ptr_factory_.GetWeakPtr()));
}
void FileSelectionCanceled() override {
select_file_dialog_ = nullptr;
std::move(import_callback_).Run(nullptr);
}
void FileRead(std::optional<std::vector<uint8_t>> file_bytes) {
if (!file_bytes) {
std::move(import_callback_)
.Run(certificate_manager_v2::mojom::ImportResult::NewError(
"error reading file"));
return;
}
(*remote_client_)
->AskForImportPassword(base::BindOnce(
&CrosClientCertSource::GotImportPassword,
weak_ptr_factory_.GetWeakPtr(), std::move(*file_bytes)));
}
void GotImportPassword(std::vector<uint8_t> file_bytes,
const std::optional<std::string>& password) {
if (!password) {
std::move(import_callback_).Run(nullptr);
return;
}
std::move(import_callback_)
.Run(certificate_manager_v2::mojom::ImportResult::NewError(
"not implemented"));
}
private:
scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
CertificateManagerPageHandler::ImportCertificateCallback import_callback_;
raw_ptr<mojo::Remote<certificate_manager_v2::mojom::CertificateManagerPage>>
remote_client_;
base::WeakPtrFactory<CrosClientCertSource> weak_ptr_factory_{this};
};
#endif
#if BUILDFLAG(IS_CHROMEOS)
class ExtensionsClientCertSource
: public CertificateManagerPageHandler::CertSource {
public:
explicit ExtensionsClientCertSource(
std::unique_ptr<chromeos::CertificateProvider> provider)
: provider_(std::move(provider)) {}
~ExtensionsClientCertSource() override = default;
void GetCertificateInfos(
CertificateManagerPageHandler::GetCertificatesCallback callback)
override {
if (!provider_) {
std::move(callback).Run({});
return;
}
if (certs_) {
PopulateCertInfosFromCertificateList(std::move(callback), *certs_);
return;
}
provider_->GetCertificates(
base::BindOnce(&ExtensionsClientCertSource::SaveCertsAndRespond,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ViewCertificate(
const std::string& sha256_hex_hash,
base::WeakPtr<content::WebContents> web_contents) override {
if (!provider_ || !certs_) {
return;
}
ViewCertificateFromCertificateList(sha256_hex_hash, *certs_,
std::move(web_contents));
}
private:
void SaveCertsAndRespond(
CertificateManagerPageHandler::GetCertificatesCallback callback,
net::ClientCertIdentityList cert_identities) {
certs_ = net::CertificateList();
certs_->reserve(cert_identities.size());
for (const auto& identity : cert_identities) {
certs_->push_back(identity->certificate());
}
PopulateCertInfosFromCertificateList(std::move(callback), *certs_);
}
std::unique_ptr<chromeos::CertificateProvider> provider_;
std::optional<net::CertificateList> certs_;
base::WeakPtrFactory<ExtensionsClientCertSource> weak_ptr_factory_{this};
};
#endif
}
std::unique_ptr<CertificateManagerPageHandler::CertSource>
CreatePlatformClientCertSource(
mojo::Remote<certificate_manager_v2::mojom::CertificateManagerPage>*
remote_client) { … }
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
std::unique_ptr<CertificateManagerPageHandler::CertSource>
CreateProvisionedClientCertSource(Profile* profile) { … }
#endif
#if BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<CertificateManagerPageHandler::CertSource>
CreateExtensionsClientCertSource(Profile* profile) {
chromeos::CertificateProviderService* certificate_provider_service =
chromeos::CertificateProviderServiceFactory::GetForBrowserContext(
profile);
return std::make_unique<ExtensionsClientCertSource>(
certificate_provider_service->CreateCertificateProvider());
}
#endif